icpc泉城联赛第三次训练赛
1.线段树仍在学习中
2.比赛的时候这个题没归我管,现进行赛后补题;
题目大意:给定一个长度为n的字符串,要求打印第k字典序最小的字符出(k
并且给出了字符串a字典序小于字符串b的定义:
(1)存在一个i使得a[i]
(2)a字符串是b字符串的子集
题目要给出n个字符串来找第k小的字符串(在主串中找匹配串),并且注意到题目说输入的时候先输入的字母是比后输入的字母小,是递减的趋势;
例如,第一行中的acb···xyz表示字符“c”小于字符“b”,而字符“c”大于字符“a”;
单靠ascll字符是不行的,需要用一个数组来记录他们的大小,除此之外,要找满足条件的字符串需要以主串为准来对子串进行一个有规则的排序;
这个规则就是上面说的字典序小的规则;
然后输出就可以了,并且这道题目存在使用string,千万小心,
由于个人电脑原因vjuidge打不开,所以只有题目测试样例过了的代码,正确性仍需检验:
1 #include2 using namespace std; 3 int n,k; 4 string s; 5 int num[1010];//储存字母大小 6 string ch[1010];//n个子串 7 bool cmp(string a,string b)//字典序规则 8 { 9 int len1=a.length(); 10 int len2=b.length(); 11 for(register int i=0,j=0;i 12 { 13 if(num[a[i]]<num[b[j]]) 14 return true; 15 else if(num[a[i]]>num[b[j]]) 16 return false; 17 } 18 if(len1<len2) 19 return true; 20 else 21 return false; 22 } 23 int main() 24 { 25 /*std::ios::sync_with_stdio(false); 26 cin.tie(0); 27 cout.tie(0)*/; 28 getline(cin,s); 29 for(register int i=0;i<26;i++) 30 { 31 num[s[i]]=i;//储存 32 } 33 cin>>n; 34 cin.get();//注意空格 35 for(register int i=0;i) ) 36 { 37 getline(cin,ch[i]); 38 } 39 cin>>k; 40 sort(ch,ch+n,cmp); 41 cout< 1]< //第k小 42 } 43 /*acbdefghijklmnopqrstuvwxyz 44 2 45 abc 46 acb 47 1 48 */
c依旧是不会,dfs这玩意用两个vis数组不行吗?
放上失败代码,以后有空研究这个题;
1 #include2 using namespace std; 3 char ch[100][100]; 4 int n; 5 int mint=0x3f3f3f3f; 6 const int INF=0x3f3f3f3f; 7 bool check(int x, int y){ 8 if(x < 0 || y < 0 || x >= n || y >= n) 9 return false; 10 return true; 11 } 12 int dir[5][2]= 13 { 14 {-1,0}, 15 {0,-1}, 16 {1,0}, 17 {0,1}, 18 }; 19 bool vis1[100][100]; 20 bool vis2[100][100]; 21 void dfs(int sx,int sy,int tx,int ty,int step) 22 { 23 vis1[sx][sy]=true; 24 vis2[tx][ty]=true; 25 if(sx==tx&&sy==ty) 26 { 27 mint=min(mint,step); 28 return ; 29 } 30 for(register int i=0;i<4;i++) 31 { 32 int newax=sx+dir[i][0]; 33 int neway=sy+dir[i][1]; 34 int newbx=tx+dir[i][0]; 35 int newby=ty+dir[i][1]; 36 if(check(newax,neway)&&check(newbx,newby)&&ch[newax][neway]=='.'&&ch[newbx][newby]=='.') 37 { 38 if(!vis1[newax][neway]&&!vis2[newbx][newby]) 39 { 40 dfs(newax,neway,newbx,newby,step+1); 41 } 42 } 43 else if(check(newax,neway)&&ch[newax][neway]=='.') 44 { 45 if(!vis1[newax][neway]) 46 { 47 dfs(newax,neway,tx,ty,step+1); 48 } 49 } 50 else if(check(newbx,newby)&&ch[newbx][newby]=='.') 51 { 52 if(!vis2[newbx][newby]) 53 { 54 dfs(sx,sy,newbx,newby,step+1); 55 } 56 } 57 else 58 continue; 59 } 60 } 61 int main() 62 { 63 std::ios::sync_with_stdio(false); 64 cin.tie(0); 65 cout.tie(0); 66 int xa,ya,xb,yb; 67 cin>>n; 68 for(register int i=0;i 69 { 70 for(register int j=0;j) ) 71 { 72 cin>>ch[i][j]; 73 if(ch[i][j]=='a') 74 { 75 xa=i; 76 ya=j; 77 } 78 if(ch[i][j]=='b') 79 { 80 xb=i; 81 yb=j; 82 } 83 } 84 } 85 dfs(xa,ya,xb,yb,0); 86 if(mint<INF) 87 { 88 cout< endl; 89 } 90 else 91 { 92 cout<<"no solution"<<endl; 93 } 94 return 0; 95 }
d
题目大意:给定一个数组,求a[i]%a[j]的最大值,i,j满足1<=i,j<=n&&a[i]>=a[j]
对于所有的 ai 找到 k * ai 和 (k + 1) * aj , (k = 1 2 3....) 对于 ai 能 取到最大值的肯定是 k * ai 和 (k + 1) * aj 每个区间中的最大值 mod ai 取到数然后取最大值,这是官方给的解释那很明显我们要求的是a[i]=k*a[j]+x,ans=x;
题目说要求余数的最大值,所以我们要做一个排序的前提下还要求k
以下是试错的过程:
从大到小枚举a[j]后,枚举倍数k
首先暴力过不了,挨个枚举试过了,不行
贪心试过了,听别人说剪枝也不行;
二分可以试试,每次查找求最近与a[j]的倍数k
其实贪心是可行的,二分也是可行的,甚至官方都说筛法也是可行的
我的做法是
对数组排序之后从后往前找,如果x已经大于下一个a[j]-1则停止
对于每一个a[j]用二分找到它每个倍数对应的a[i]
那a[i-1]就是我们想要的最大的余数
这里才用反向查找的目的是为了如果后序发现了当前数值和前一个数值出现重复判断则进行剪枝测率
其实也可以用unique去重
还有一个就是正向的我已经跑过了,所以说采取反向的查找测率可以在一定程度上降低一定的时间复杂度
1 #include2 using namespace std; 3 const int num=3e5; 4 int a[num]; 5 int n; 6 int s; 7 int ans; 8 int main() 9 { 10 std::ios::sync_with_stdio(false); 11 cin.tie(0); 12 cout.tie(0); 13 cin>>n; 14 for(register int i=1;i<=n;i++) 15 { 16 cin>>a[i]; 17 } 18 sort(a+1,a+1+n);//为了查找接近与a[j]的倍数k 19 /*for(register int i=1;i<=n;i++) 20 { 21 if(a[i]==a[i-1]) 22 { 23 continue; 24 } 25 else 26 { 27 a[i]=a[i-1]+1; 28 } 29 for(register int j=2*a[i];j<=a[n];j+=a[n]) 30 { 31 //s=upper_bound(a+1,a+1+n,j)-a; 32 s=lower_bound(a+1,a+1+n,j)-a; 33 ans=max(ans,a[s]%a[i]); 34 } 35 ans=max(ans,a[n]%a[i]); 36 }*/ 37 for(register int i=n;i>=1;i--) 38 { 39 if(ans>a[i]-1)//找到了立刻停止 40 break; 41 if(i 剪枝 42 { 43 if(a[i]==a[i+1]) 44 continue; 45 } 46 for(register int j=2*a[i];j<=a[n];j+=a[i])//从2*a[i]开始,枚举倍数k 47 { 48 s=lower_bound(a+1,a+1+n,j)-a;//二分查找 49 if(s==0) 50 continue; 51 s--; 52 ans=max(ans,a[s]%a[i]);//更新区间 53 } 54 ans=max(ans,a[n]%a[i]);//求最大余数 55 } 56 cout<<ans; 57 return 0; 58 }//
efgh没归我管,暂时没空补题;
i题
学习了一种新的筛法
即:
1 int is_prime(int n1)
2 {
3 if(n1==2||n1==3)
4 return 1;
5 if(n1%6!=1&&n1%6!=5)
6 return 0;
7 for(register int i=5;i<=floor(sqrt(n1));i+=6)
8 {
9 if(n1%i==0||n1%(i+2)==0)
10 return 0;
11 }
12 return 1;
13 }
这种筛素数的方法比普通版快,但是埃氏快不快就不知道了,然后就是筛和选数字了,这里有一个取巧的方法就是先筛出素数来在选数,这样远比判筛边选书要快
还是受别人之启发哈哈哈
失败代码:(tle绝对了)
1 #include2 using namespace std; 3 typedef long long ll; 4 ll ans; 5 ll n; 6 bool is_prime(long long n) 7 { 8 if(n<=1) 9 return false; 10 for(register ll i=2;i<=sqrt(n);i++) 11 { 12 if(n%i==0) 13 return false; 14 } 15 return true; 16 } 17 int main() 18 { 19 std::ios::sync_with_stdio(false); 20 cin.tie(0); 21 cout.tie(0); 22 cin>>n; 23 for(register ll q=2;q<=sqrt(n);q++) 24 { 25 for(register ll p=2;p 26 { 27 for(register ll i=2;i<=n;i++) 28 if(p*pow(q,3)==i) 29 { 30 if(is_prime(p)&&is_prime(q)) 31 { 32 ans++; 33 } 34 } 35 } 36 } 37 cout<<ans; 38 return 0; 39 })
AC代码:
1 #include2 using namespace std; 3 const int num=1e6; 4 typedef long long ll; 5 int a[num]; 6 ll ans; 7 ll n; 8 int is_prime(int n1)//is_prime+(plus) 9 { 10 if(n1==2||n1==3) 11 return 1; 12 if(n1%6!=1&&n1%6!=5) 13 return 0; 14 for(register int i=5;i<=floor(sqrt(n1));i+=6) 15 { 16 if(n1%i==0||n1%(i+2)==0) 17 return 0; 18 } 19 return 1; 20 } 21 22 int main() 23 { 24 std::ios::sync_with_stdio(false); 25 cin.tie(0); 26 cout.tie(0); 27 cin>>n; 28 ll flag=1; 29 for(register ll i=2;i<=num;i++)//先筛出来 30 { 31 if(is_prime(i)==1) 32 { 33 a[flag]=i; 34 flag++; 35 } 36 } 37 ll L=1; 38 ll R=flag-1; 39 if(n<8) 40 cout<<"0"<<endl; 41 else 42 { 43 44 while(L 选数 45 { 46 if(a[L]*pow(a[R],3)>n) 47 { 48 R--; 49 } 50 else 51 { 52 ans+=(R-L); 53 L++; 54 } 55 } 56 cout<// endl; 57 } 58 return 0; 59 }
j没归我管,以后补题
k题
题目大意:给定一张有向图(DAG),求从某点开始能无限循环的点的个数。
也就是说一个点如果可以从这里出发然后无穷尽的走下去,这种点就是我们需要的点
因为没有出边的点必定是环而且只有通
向环出边的点是环路。
所以说首先那些出度为0的点不行所以说,所以我们考虑建立反图
因为环是不随边的正反性而发生变化,我们如果对这个图进行一次拓扑一来可以从终点到起点,省去了终点不定的问题,二来我们可以得到反图不能到的点,
而这种点的求法恰好是所有点的个数减去所有通向环的点的个数再以所有点的个数
其实一上来我采取的是正向跑图的方法,因为做的时候组内有人画出了题目大体的图,既然题目要求环路的话则拓扑排序不失为一个判环的好方法,而正向跑图却忽略了一些点我们是到达不了的
而且还在入度的细节方面出了问题
错误代码:
1 #include2 using namespace std; 3 int n,m; 4 const int num=2e5+10; 5 vector<int>edge[num]; 6 int in[num]; 7 vector<int>topo; 8 queue<int>q; 9 int ans; 10 int main() 11 { 12 std::ios::sync_with_stdio(false); 13 cin.tie(0); 14 cout.tie(0); 15 cin>>n>>m; 16 for(register int i=1;i<=m;i++) 17 { 18 int a,b; 19 cin>>a>>b; 20 edge[a].push_back(b); 21 in[a]++; 22 } 23 for(register int i=1;i<=n;i++) 24 if(!in[i]) 25 { 26 q.push(i); 27 } 28 while(!q.empty()) 29 { 30 int x=q.front(); 31 q.pop(); 32 topo.push_back(x); 33 ans++; 34 for(register int k=0;k 35 { 36 int b=edge[x][k]; 37 if(!in[b]--) 38 q.push(b); 39 } 40 } 41 ans=n-ans; 42 cout<) endl; 43 return 0; 44 }
AC代码如下:
1 #include//icpc泉赛twice k 2 using namespace std; 3 int n,m; 4 const int num=2e5+10; 5 vector<int>edge[num]; 6 int in[num]; 7 //vector 35 { 36 int b=edge[x][k]; 37 in[b]--; 38 if(!in[b]) 39 q.push(b); 40 } 41 } 42 ans=n-ans;//所有的点减去环路点即是结果 43 cout<topo; 8 queue<int>q; 9 int ans; 10 int main() 11 { 12 std::ios::sync_with_stdio(false); 13 cin.tie(0); 14 cout.tie(0); 15 cin>>n>>m; 16 for(register int i=1;i<=m;i++) 17 { 18 int a,b; 19 cin>>a>>b; 20 edge[b].push_back(a); 21 in[a]++; 22 } 23 for(register int i=1;i<=n;i++) 24 if(!in[i]) 25 { 26 q.push(i); 27 } 28 while(!q.empty()) 29 { 30 int x=q.front(); 31 q.pop(); 32 //topo.push_back(x); 33 ans++; 34 for(register int k=0;k) endl; 44 return 0; 45 }
L题没必要,还没接触到莫队
总结:
题目难度有一定的思维量和难度,但比起省赛还是差了点意思
这次做题还是发现了一些问题,就是对于一些优化方法上的空缺
最重要的是我终于把学习线段树提上了日程
然后就是基础算法的一些强化和变形仍需学习
这就需要刷大量的算法题来应对不同的算法变形
也要刷一定的思维题来保证思维的活跃