CF26E
一道很有趣的题...难度并不是很大,基本可以接受
显然这是一个构造问题
我们分成几类进行讨论:
首先,如果目标是非正数,那一定是不可能的(显而易见)
然后,如果目标比允许操作的和还要大,那也是不可能的
剩下的情况就不那么显然了,我们先假设有解再进行讨论
(插播一句:强烈建议食用样例!!!对正解有很大启发!!!)
首先,如果需要构造的值比所有操作数中的最小值还要小(如样例2),怎么办?
样例2给出了较为明确的思想:
我们记录两个位置,一个是最小操作数的位置,另一个随意
我们的目标是用最小操作数的位置来构造出答案
我们在第二个位置上先记录一下一开始的值,然后把剩余所有位置上多余的部分全跑完,但是把最小操作数的位置上保留足够构造出答案的操作数
然后在第二个位置上再点一次,这样就把$y$更新回了1
然后在最小值的位置上记录下当前$y$的值,再把第二个位置多余的操作跑完
再在最小值位置点完,就能成功构造出目标了
可是这里有一个问题:如果目标是1呢?这样做出来能构造出的目标至少是2啊!
所以如果目标是$1$,却没有任何一个操作数是1,那么就是无解的!
如果有呢?
那种可以和下一种情况合并
下一种情况自然就是需要构造的值比最小值要大
这样的话,最小值即使跑满也不会超标
因此我们在最小值的位置上先记录下$y$,然后把别的多余的部分跑满,最后利用原来记录的位置把$y$改回去,然后跑满即构造出了答案
这样就结束了
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> int num[105]; int n,w; int sum=0,minv=0x3f3f3f3f; int main() { freopen("brand.in","r",stdin); freopen("brand.out","w",stdout); scanf("%d%d",&n,&w); int posi=0; for(int i=1;i<=n;i++) { scanf("%d",&num[i]); sum+=num[i]; if(minv>num[i])minv=num[i],posi=i; } if(w<=0||sum<w){printf("Toushui\n");return 0;} if(w<minv) { if(w==1){printf("Toushui\n");return 0;} int pp=0; for(int i=1;i<=n;i++)if(i!=posi&&num[i]){pp=i;num[i]--;break;} if(!pp){printf("Toushui\n");return 0;} printf("Yuyue\n"); printf("%d ",pp); while(num[posi]+1>w)printf("%d %d ",posi,posi),num[posi]--; for(int i=1;i<=n;i++) { if(i==pp||i==posi)continue; while(num[i])printf("%d %d ",i,i),num[i]--; } printf("%d %d ",pp,posi); while(num[pp])printf("%d %d ",pp,pp),num[pp]--; printf("%d ",posi),num[posi]--; while(num[posi])printf("%d %d ",posi,posi),num[posi]--; printf("\n"); }else { printf("Yuyue\n"); printf("%d ",posi); int res=sum-w; for(int i=1;i<=n;i++) { if(i==posi)continue; while(num[i]) { if(res)printf("%d %d ",i,i),res--,num[i]--; if(!res)break; } if(!res)break; } printf("%d ",posi); num[posi]--; for(int i=1;i<=n;i++)while(num[i])printf("%d %d ",i,i),num[i]--; printf("\n"); } return 0; }