Do Not Try This Problem Gym - 102307D
题意:给个长度为len的字符串(len<=1e5),然后q次操作(q<=1e5),每次给出i,a,k,c,(i+k*a<=len)也就是把字符串位置为i,i+a,i+2*a...,i+k*a的改成c字符,输出q次操作后的字符串。
首先暴力做法肯定是遍历字符串的每个位置,然后从后往前遍历,找到最后一个修改到这个位置的操作,但这样的时间复杂度是n*q,是1e10级别的,很明显不行。然后看提交里一位大佬的代码,才明白是怎么个做法。
我们的入手点就在i+k*a<=len这里,i基本没啥影响我们不管i,直接看k*a,设n为√len,那么如果k<=n,我们就直接更新,此时是n*q,是3e7级别的。
那么对于k>n的,此时a<=n,我们就遍历a从1到n,对每个a进行考虑,相同a的按照操作次序从后往前的顺序进行遍历,因为是相同的a,那么同一个位置的下一个位置也是一样的,
所以我们可以用一个并查集的区间合并进行跳转,这样对于相同的a,字符串的每个位置只会被遍历到一个,此时是n*len*并查集find小于log的一个复杂度。
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<vector> 5 #include<algorithm> 6 using namespace std; 7 typedef long long ll; 8 const int N=1e5+11; 9 struct Node{ 10 int p,a,k,id; 11 Node(){} 12 Node(int p,int a,int k,int id):p(p),a(a),k(k),id(id){} 13 }; 14 char mmp[N],mmk[N]; 15 int ans[N],fa[N]; 16 vector<Node> vv; 17 void init(int n){ 18 vv.clear(); 19 for(int i=0;i<=n;i++) ans[i]=0; 20 } 21 int find(int x){ 22 return fa[x]==x ? x : fa[x]=find(fa[x]); 23 } 24 int main(){ 25 int lens,n,q,p,a,kk; 26 while(~scanf("%s%d",mmp+1,&q)){ 27 lens=strlen(mmp+1); 28 n=sqrt(lens); 29 init(lens); 30 for(int i=1;i<=q;i++){ 31 scanf("%d%d%d %c",&p,&a,&kk,&mmk[i]); 32 if(kk<=n){ 33 for(int j=0;j<=kk;j++) ans[p+j*a]=max(ans[p+j*a],i); 34 continue; 35 } 36 vv.push_back(Node(p,a,kk,i)); 37 } 38 for(int i=1;i<=n;i++){ 39 for(int j=1;j<=lens+1;j++) fa[j]=j; 40 for(int j=(int)vv.size()-1;j>=0;j--){ 41 if(vv[j].a!=i) continue; 42 for(int k=find(vv[j].p),lim=vv[j].p+vv[j].k*vv[j].a;k<=lim;k=find(k)){ 43 ans[k]=max(ans[k],vv[j].id); 44 fa[k]=k+i<=lens ? find(k+i) : lens+1; 45 } 46 } 47 } 48 for(int i=1;i<=lens;i++) if(ans[i]) mmp[i]=mmk[ans[i]]; 49 printf("%s ",mmp+1); 50 } 51 return 0; 52 }