梦幻布丁 P3201做题记录
P3201梦幻布丁
引子
第一次接触这道题还是在两年前,那时候连线段树都没有系统性的学习过。今天重做这道题,回顾一下这两年都有什么进步
切入
首先我们考虑最朴素的暴力
通过暴力枚举a[i]是否等于a[i-1]来计算连续染色段,之后遍历[1,n]更改颜色,复杂度为O(nm)
code:
1 // Problem: P3201 [HNOI2009] 梦幻布丁 2 // Contest: Luogu 3 // URL: https://www.luogu.com.cn/problem/P3201 4 // Memory Limit: 128 MB 5 // Time Limit: 1000 ms 6 // 7 // Powered by CP Editor (https://cpeditor.org) 8 9 #include<bits/stdc++.h> 10 #define int long long 11 #define endl '\n' 12 #define endll " " 13 #define it inline int 14 #define fre(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout); 15 #pragma GCC optimize(0) 16 #pragma GCC optimize(2) 17 #pragma GCC optimize(3,"Ofast","inline") 18 using namespace std; 19 const int MAXN=500050; 20 const int INF=0x3f3f3f3f; 21 it gcd(int x,int y){return y==0?x:gcd(y,x%y);} 22 it lcm(int x,int y){return x/gcd(x,y)*y;} 23 it max(int x,int y){return x>y?x:y;} 24 it min(int x,int y){return x<y?x:y;} 25 it qpow(int x,int m,int mod) 26 { 27 int res=1,bas=x%mod; 28 while(m) 29 { 30 if(m&1) res=(res*bas)%mod; 31 bas=(bas*bas)%mod,m>>=1; 32 } 33 return res%mod; 34 } 35 int n,m,u,v,w,l,r,ans,cnt,sum,tot,num,a[MAXN],opt,x,y; 36 signed main() 37 { 38 //fre("P3201"); 39 ios::sync_with_stdio(false); 40 cin.tie(0); 41 cout.tie(0); 42 cin >> n >> m; 43 for(int i=1;i<=n;i++) 44 cin >> a[i]; 45 while(m--) 46 { 47 cin >> opt; 48 if(opt==1) 49 { 50 cin >> x >> y; 51 for(int i=1;i<=n;i++) 52 if(a[i]==x) 53 a[i]=y; 54 } 55 else 56 { 57 int cnt=1; 58 for(int i=2;i<=n;i++) 59 if(a[i]!=a[i-1]) 60 cnt++; 61 cout<<cnt<<endl; 62 } 63 } 64 return 0; 65 }
妥妥被T飞,开O2后有60pts
优化
很容易注意到我们有两个可以优化的点
1:不需要每次遍历整个数组来统计cnt,只需要根据每次对数组的操作所造成的对cnt的影响就可以了
2:不需要每次暴力枚举来更改染色,我们可以设计一个链状结构来对不同的颜色之间进行合并
而形同于2这样的思想正是启发式合并
假设为颜色x的链P,颜色为y的链Q
其中有L(P)<=L(Q)
那么对于将x修改为y的操作我们就可以直接将链P挂在链Q上
若L(P)>L(Q)就把Q挂在P上,同时修改将P的信息修改为y
易知每次修改的复杂度为O(min(L(P),L(Q))
而每个元素最多会被修改logn次
则有总时间复杂度为O(nlogn)
完
AC Code:
1 // Problem: P3201 [HNOI2009] 梦幻布丁 2 // Contest: Luogu 3 // URL: https://www.luogu.com.cn/problem/P3201 4 // Memory Limit: 128 MB 5 // Time Limit: 1000 ms 6 // 7 // Powered by CP Editor (https://cpeditor.org) 8 9 #include<bits/stdc++.h> 10 #define int long long 11 #define endl '\n' 12 #define endll " " 13 #define it inline int 14 #define fre(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout); 15 #pragma GCC optimize(0) 16 #pragma GCC optimize(2) 17 #pragma GCC optimize(3,"Ofast","inline") 18 using namespace std; 19 const int MAXN=5000500;//数组记得开得够大,我就是因为这个re了一发 20 const int INF=0x3f3f3f3f; 21 it gcd(int x,int y){return y==0?x:gcd(y,x%y);} 22 it lcm(int x,int y){return x/gcd(x,y)*y;} 23 it max(int x,int y){return x>y?x:y;} 24 it min(int x,int y){return x<y?x:y;} 25 it qpow(int x,int m,int mod) 26 { 27 int res=1,bas=x%mod; 28 while(m) 29 { 30 if(m&1) res=(res*bas)%mod; 31 bas=(bas*bas)%mod,m>>=1; 32 } 33 return res%mod; 34 } 35 int n,m,u,v,w,l,r,ans,cnt,sum,tot,num,a[MAXN],opt,x,y; 36 int siz[MAXN],id[MAXN],head[MAXN],nxt[MAXN],t[MAXN]; 37 signed main() 38 { 39 //fre("P3201"); 40 ios::sync_with_stdio(false); 41 cin.tie(0); 42 cout.tie(0); 43 cin >> n >> m; 44 for(int i=1;i<=n;i++) 45 { 46 cin >> a[i]; 47 if(a[i]!=a[i-1]) 48 cnt++; 49 id[a[i]]=a[i]; 50 if(!head[a[i]]) 51 t[a[i]]=i; 52 nxt[i]=head[a[i]]; //用链式前向星存 53 head[a[i]]=i; 54 siz[a[i]]++; 55 } 56 while(m--) 57 { 58 cin >> opt; 59 if(opt==1) 60 { 61 cin >> u >> v; 62 if(u==v) 63 continue; 64 if(siz[id[u]]>siz[id[v]]) 65 swap(id[u],id[v]); 66 if(!siz[id[u]]) 67 continue; 68 for(int i=head[id[u]];i;i=nxt[i]) 69 cnt-=(a[i-1]==id[v])+(a[i+1]==id[v]); 70 for(int i=head[id[u]];i;i=nxt[i]) 71 a[i]=id[v]; 72 nxt[t[id[u]]]=head[id[v]];head[id[v]]=head[id[u]];siz[id[v]]+=siz[id[u]]; 73 siz[id[u]]=t[id[u]]=head[id[u]]=0; 74 } 75 else 76 cout<<cnt<<endl; 77 } 78 return 0; 79 }

浙公网安备 33010602011771号