梦幻布丁 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 }

 

posted @ 2025-07-24 08:16  KLaneX  阅读(9)  评论(0)    收藏  举报