反物质
【问题描述】
物理学家有一种假设,世界上存在反物质,反物质遇到正常的物质会发生湮灭。
假设现在有 n 个粒子,每个粒子的种类用一个 m 以内的正整数表示。现在
要将这些粒子按一定顺序放入一个封闭空间。封闭空间最初什么都没有。
每当放进一个粒子时,若封闭空间为空或封闭空间中的粒子和放入的粒子
种类相同,这个粒子将留在封闭空间中;若封闭空间中的粒子和放入的粒子种
类不同,则封闭空间中会有一个粒子和放入的粒子抵消(即湮灭)。
判断是否存在一种排序方案,使得最后封闭空间中有种类编号为“1”的粒
子存在。若存在,最大化最后种类编号为“1”的粒子个数。若多种方案,要求
字典序最小。
【输入格式】
第 1 行:n 和 m,用空格隔开。
第 2 到 m+1 行:第 i+1 行代表第 i 种粒子有多少个。每种粒子至少有 1 个。
保证粒子总数是 n。
【输出格式】
第 1 行:如果最后封闭空间中可以有编号为“1”的粒子存在,输出 YES,否则输出 NO。
如果第一行输出了 YES,还需继续输出:
第 2 行:这一行输出最后“1”的个数。
第 3…n+2 行:输出在能最后“1”有最大数的排序方案里,字典序最小的方案。
如果第一行输出了 NO,就不必输出其他内容了
【样例输入】
5 3
2
1
2
【样例输出】
YES
1
1
3
2
3
1
【数据规模和约定】
对于 30%的数据, n<=10
对于 60%的数据, n<=1000
对于 100%的数据, 1<=m<=n<=10^6
考场解题:
哎呦呦呦,这题只输出最多剩下多少还好,这按字典序排列可就
难为死人啦,这咋排,管他呢,先判断下‘NO’的情况吧,说不定还
能的点分,如果某种粒子的个数超过了总个数的一半,那么一定不能
有‘1’粒子,与之共存,则输出‘NO’ ,此时还需要注意者最多的是
不是 ‘1’ 粒子, 也就到这思路靠谱点, 后边的瞎想就不在此多说了,
总之就是看啥顺眼打打试试吧。
预计得分:10-20(还不得有个‘NO’送点分)
实际得分:0(没爱了)
正解:
代码理解吧:

#include<cstdio> #include<algorithm> using namespace std; const int N = 1000500; int n,m; int num[N]; int big=0; int cc[N],xu[N]; int last[N]; bool cmp(int x, int y) { return(num[x]<num[y]); } void print() { int i,sum=0; for(i=1; i<=m; i++) xu[i]=i; sort(xu+1,xu+m+1,cmp); /* for(i=1;i<=m;i++) cc[num[i]]++; //cc[x]表示粒子数量为x的有几种 for(i=1;i<=n;i++) cc[i]+=cc[i-1]; //cc[x]表示粒子数量不超过x的有几种 for(i=1;i<=m;i++) xu[cc[num[i]]--]=i; //把各种粒子按照num[i]从小到大排序 */ for(i=1; i<=m; i++) { last[i]=num[i];//last剩下多少个i这种粒子 sum+=last[i];//还有多少该抵消的粒子没抵消 } int top=m; //xu中下标超过top的,num都大于sum/2 int tot=0; //现在封闭空间中有多少个粒子,粒子编号小于s int s=1; //现在还剩下的粒子中编号最小的 while(1) { while(s<=m && !last[s]) s++; if(s>m) break; if(!tot) { tot=last[s]; last[s]=0; for(i=1; i<=tot; i++) printf("%d ",s); } else { sum-=2; tot--; while(top && num[xu[top]]>sum/2) top--; for(i=m; i>top; i--) { if(last[xu[i]]>sum/2) break; } if(i>top) { printf("%d ",xu[i]); last[xu[i]]--; } else { printf("%d ",s); last[s]--; } } } } int main() { freopen("anti.in","r",stdin); freopen("anti.out","w",stdout); int i; scanf("%d%d%d",&n,&m,&num[1]); for(i=2; i<=m; i++) { scanf("%d",&num[i]); if(num[i]*2>n-num[1]) big=i; // 找出最大种类的数目 } if(big) { int remain=num[big]-(n-num[1]-num[big]); if(num[1]>remain) { if(m>2) //如果两类,先输出1 2 结果都一样,字典序最小先输出 1 { int t=num[1]-remain; num[1]=remain; printf("YES %d ",t); print(); for(i=1; i<=t; i++) printf("1 "); } else { int t=num[1]-remain; //t表示要先输出多少个 1 printf("YES %d ",t); for(i=1; i<=num[1]; i++) printf("1 "); for(i=1; i<=num[2]; i++) printf("2 "); //最后输出几个 1 } } else printf("NO "); //若big占比重大, 可以抵消全部的1,则输出‘NO’; } else { int t; if((n-num[1])%2==1) { if(num[1]<=1) { printf("NO "); return 0; } t=num[1]-1; num[1]=1; } else { if(num[1]<1) { printf("NO "); return 0; } t=num[1]; num[1]=0; } printf("YES %d ",t); print(); for(i=1; i<=t; i++) printf("1 "); } return 0; }