bzoj1483
一道挺有意思的题目
我们先预处理出答案,对每种颜色都用一个链表进行维护,操作1就是将两个链表进行合并,并维护答案,合并时应将size较小的合并至较大的(这样可以保证log n的复杂度,因为每次合并至少会使size较小的变为原来的两倍,但是有个问题,我们必须搞一个数组p[i]来表示我们查询颜色i时应该查找的真正颜色),然后就没了。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<ctime> 5 #include<cmath> 6 #include<iostream> 7 #include<algorithm> 8 #include<queue> 9 #include<stack> 10 #include<set> 11 #define clr(a,x) memset(a,x,sizeof(a)) 12 #define rep(i,l,r) for(int i=(l);i<(r);i++) 13 using namespace std; 14 typedef long long ll; 15 typedef pair<int,int> pii; 16 #define mkp(a,b) make_pair(a,b) 17 int read(){ 18 int ans=0,f=1; 19 char c=getchar(); 20 while(!isdigit(c)){ 21 if(c=='-') f=-1; 22 c=getchar(); 23 } 24 while(isdigit(c)){ 25 ans=ans*10+c-'0'; 26 c=getchar(); 27 } 28 return ans*f; 29 } 30 const int maxn=100009,maxk=1000009; 31 int ans,n,m,a[maxn],p[maxk],s[maxk],fir[maxk],last[maxk],next[maxn]; 32 void modify(int&x,int&y){ 33 if(s[x]<s[y]) swap(x,y); 34 if(!s[y]) return; 35 for(int i=fir[y];i;i=next[i]){ 36 if(a[i-1]==x) ans--; 37 if(a[i+1]==x) ans--; 38 } 39 for(int i=fir[y];i;i=next[i]) a[i]=x; 40 s[x]+=s[y];s[y]=0; 41 next[last[x]]=fir[y];last[x]=last[y];fir[y]=last[y]=0; 42 } 43 int main(){ 44 rep(i,1,maxk+1) p[i]=i; 45 n=read();m=read(); 46 rep(i,1,n+1){ 47 a[i]=read(); 48 if(a[i]!=a[i-1]) ans++; 49 if(!last[a[i]]) last[a[i]]=i; 50 next[i]=fir[a[i]]; 51 fir[a[i]]=i;s[a[i]]++; 52 } 53 while(m--){ 54 int opt=read(); 55 if(opt==1){ 56 int x=read(),y=read(); 57 if(x!=y) modify(p[y],p[x]); 58 }else printf("%d\n",ans); 59 } 60 return 0; 61 }
1483: [HNOI2009]梦幻布丁
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1689 Solved: 715
[Submit][Status][Discuss]
Description
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
Input
第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0
Output
针对第二类操作即询问,依次输出当前有多少段颜色.
Sample Input
4 3
1 2 2 1
2
1 2 1
2
1 2 2 1
2
1 2 1
2
Sample Output
3
1
1