[nowcoder5669E]Eliminate++

枚举$a_{i}$并判断是否可行,有以下结论:若$a_{i}$可以留下来,一定存在一种合法方案使得$a_{i}$仅参与最后若干次合并,且第一次参与合并前左右都不超过2个数
证明:将大于$a_{i}$的看成1,小于$a_{i}$的看成0,将合并分为两类:
1.都在左/右区间,那么相当于删除了最右/左边的$01/10$,若存在3个数,可以将倒数第3个数与$01/10$合并去删除
2.左右区间各有1个,当达到3个后,不管是$000$和$111$还是$010$和$101$都可以预先合并再与$a_{i}$合并
根据上述结论,我们可以对两边分别求出最终结果能否为$0,00,1,11,01/10$,并判断能否组合即可
(可以保留$a_{i}$的组合方法有8种:$0-1$,$1-0$,$00-11$,$11-00$,$01/10-01/10$)
 对这些结果分为两类进行判断:
1.全部相同(即$0/00/1/11$):以0为例,不断插入一个数字,要合并当且仅当以$111$或$10$为结尾(特别的,如果$10$是最后两个数,那么就无解,也可以删除掉),证明略(贪心策略中$10$消除是为了更好地消除$111$)
具体维护方法:根据贪心,可以发现插入并合并完后一定是若干个$0$和不超过2个$1$,对0和1的个数进行维护,最后只需要判断0的个数是否比1的多即可
2.$01/10$,首先长度必须要为偶数,之后考虑如果$0$和$1$个数相同,那么一定存在相邻的$01/10$且必然有别的数(否则就已经完成了),不断消除$01/10$即可
假设$1$的个数比$0$的个数多,按照第一种方案的贪心,有解必然存在一次合并后满足$0$的个数和$1$的个数一样多(包括未插入的部分),即最终状态下$0$的个数不小于比$1$的个数,证明略
但这样的复杂度为$o(n^{2})$,需要优化:
从小到大枚举$a_{i}$,那么每一次相当于将一个位置上的$1$变成$0$,考虑要维护哪些信息:1.两种贪心时的最终状态;2.$0$的个数和$1$的个数
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 1000005
 4 #define L (k<<1)
 5 #define R (L+1)
 6 #define mid (l+r>>1)
 7 struct sta{
 8     int x,y;
 9 };
10 struct ji{
11     int k;
12     sta a[2];
13 }o,o0,o1;
14 struct type{
15     ji p0,p1;
16 }f[N<<2];
17 int t,n,a[N],id[N],ans[N];
18 sta merge(sta x,sta y){
19     sta z;
20     z.x=x.x+max(y.x-x.y,0);
21     z.y=(y.y+max(x.y-y.x,0)-1)%2+1;
22     return z;
23 }
24 type merge(type x,type y){
25     type z;
26     z.p0.k=x.p0.k+y.p0.k;
27     z.p1.k=x.p1.k+y.p1.k;
28     z.p0.a[0]=merge(x.p0.a[0],y.p0.a[0]);
29     z.p0.a[1]=merge(y.p0.a[1],x.p0.a[1]);
30     z.p1.a[0]=merge(x.p1.a[0],y.p1.a[0]);
31     z.p1.a[1]=merge(y.p1.a[1],x.p1.a[1]);
32     return z;
33 }
34  
35 void build(int k,int l,int r){
36     if (l==r){
37         f[k]=type{o0,o1};
38         return;
39     }
40     build(L,l,mid);
41     build(R,mid+1,r);
42     f[k]=merge(f[L],f[R]);
43 }
44 void update(int k,int l,int r,int x){
45     if (l==r){
46         f[k]=type{o1,o0};
47         return;
48     }
49     if (x<=mid)update(L,l,mid,x);
50     else update(R,mid+1,r,x);
51     f[k]=merge(f[L],f[R]);
52 }
53 type query(int k,int l,int r,int x,int y){
54     if ((l>y)||(x>r))return type{o,o};
55     if ((x<=l)&&(r<=y))return f[k];
56     return merge(query(L,l,mid,x,y),query(R,mid+1,r,x,y));
57 }
58 bool pd(type k,int p,int x){
59     if (!x)return k.p0.a[p].x>k.p0.a[p].y;
60     if (x==1)return k.p1.a[p].x>k.p1.a[p].y;
61     if (x==2){
62         if (k.p0.k==k.p1.k)return 1;
63         if (k.p0.k<k.p1.k)return k.p0.a[p].x>=k.p0.a[p].y;
64         return k.p1.a[p].x>=k.p1.a[p].y;
65     }
66 }
67 int main(){
68     o0.a[0].y=o0.a[1].y=1;
69     o1.k=o1.a[0].x=o1.a[1].x=1;
70     scanf("%d",&t);
71     while (t--){
72         scanf("%d",&n);
73         for(int i=1;i<=n;i++){
74             scanf("%d",&a[i]);
75             ans[i]=0;
76             id[a[i]]=i;
77         }
78         build(1,1,n);
79         for(int i=1;i<=n;i++){
80             update(1,1,n,id[i]);
81             type x=query(1,1,n,1,id[i]-1),y=query(1,1,n,id[i]+1,n);
82             if ((pd(x,0,0))&&(pd(y,1,1))||(pd(x,0,1))&&(pd(y,1,0))||(pd(x,0,2))&&(pd(y,1,2)))ans[id[i]]=1;
83         }
84         for(int i=1;i<=n;i++)printf("%d",ans[i]);
85         printf("\n");
86     }
87 }
View Code

 

posted @ 2020-08-02 11:31  PYWBKTDA  阅读(175)  评论(0编辑  收藏  举报