[atAGC053D]Everyone is a winner
下面将直接叙述本题的做法——
维护序列$\{T_{i}\}$,初始$T_{i}$为(强制所有人均按时间倒序做题时)第一个解决前$i$道题的时间
从后往前考虑每一个人,对第$i$个人按如下方式确定其解决题目的顺序:
1.显然前$i$道题和后$n-i$道题内均倒序做题,因此仅需要确定前$i$道题(的分布)即可
2.设前$i$道题中分别选择$x,y$和$z$道时间为1,2和3的题目,在$t=x+2y+3z\le T_{i}$的基础上,依次最大化$t$和$x$
记$S_{i,j}$为第$i$个人解决前$j$道题的时间,那么将$\forall 1\le j<i,T_{j}=\min (T_{j},S_{i,j})$
另外,无解的情况即在第2步中无法选出$i$道题使得$t\le T_{i}$
关于上述做法的正确性,并不太会证
关于实现,注意到$T_{i}$和$S_{j,i}$均可表示为斜率$\in \{1,2,3\}$的凸包(包括初始的$T_{i}$),进而即可维护
时间复杂度为$o(n)$,可以通过
(代码中为了方便,最大化$t$时暴力减小$t$并判定,注意到每次$t$递减,因此均摊是$o(n)$的)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 #define oo 0x3f3f3f3f 5 struct Line{ 6 int a,b,c; 7 Line(int aa=oo,int bb=oo,int cc=oo){ 8 a=aa,b=bb,c=cc; 9 } 10 int get_val(int x){ 11 return min(min(a+x,b+2*x),c+3*x); 12 } 13 }T; 14 int t,n,a[N],b[N],c[N]; 15 Line merge(Line x,Line y){ 16 return Line(min(x.a,y.a),min(x.b,y.b),min(x.c,y.c)); 17 } 18 int main(){ 19 scanf("%d",&t); 20 while (t--){ 21 scanf("%d",&n); 22 T=Line(); 23 for(int i=1;i<=n;i++){ 24 scanf("%d%d%d",&a[i],&b[i],&c[i]); 25 T=merge(T,Line(b[i]+2*c[i],c[i],0)); 26 } 27 bool flag=0; 28 for(int i=n;i;i--){ 29 //y+z=i-x,2y+3z=t-x, 30 //y=3i-t-2x,z=t+x-2i 31 //0<=x<=a[i],0<=y<=b[i],0<=z<=c[i] 32 //(3i-t-b[i]+1)/2<=x<=(3i-t)/2 33 //2i-t<=x<=c[i]+2i-t 34 int t=T.get_val(i),x; 35 while (t>=0){ 36 x=min(min(a[i],(3*i-t)/2),c[i]+2*i-t); 37 if (x>=max(max(0,(3*i-t-b[i]+1)/2),2*i-t))break; 38 t--; 39 } 40 if (t<0){ 41 flag=1; 42 break; 43 } 44 int y=3*i-t-2*x,z=t+x-2*i; 45 T=merge(T,Line(y+2*z,z,0)); 46 } 47 if (flag)printf("No\n"); 48 else printf("Yes\n"); 49 } 50 return 0; 51 }