1. 程式人生 > 實用技巧 >[loj2850]無進位加法

[loj2850]無進位加法

(似乎漏了一個數據範圍,cf上的題面中還有$\sum L\le 3\cdot 10^{5}$)

考慮$a_{i}=2^{k_{i}}$時(不妨$k_{1}\ge k_{2}\ge ...\ge k_{n}$),記$\sum_{i=1}^{n}b_{i}$的最高位為$L_{b}$,則有$L_{b}=\max_{i=1}^{n}(k_{i}+i-1)$

證明:大於等於$2^{k_{i}}$的$b_{i}$至少要$i$個,因此該值即為下限;取$b_{i}=2^{L_{b}-i+1}\ge 2^{k_{i}}$,因此一定可行

利用上面的這個結論,我們開始考慮正解

如果令$k_{i}$表示$a_{i}$最高的二進位制位,那麼$2^{k_{i}}\le a_{i}<2^{k_{i}+1}$,取$a'_{i}=2^{k_{i}+1}$,設此時$\sum_{i=1}^{n}b_{i}$的最高為$L_{b}+1$,$a_{i}$減小$b_{i}$不增,因此答案中$\sum_{i=1}^{n}b_{i}$的最高位不超過$L_{b}+1$

令$t=\min_{k_{i}+i=L_{b}}i$,考慮答案($\sum_{i=1}^{n}b_{i}$)的第$[k_{t},L_{b}+1]$位(共$t+1$位),必然存在$t$位為1(每一個1最多消除一個$a_{i}$,而存在$t$個$a_{i}$最高位大於等於$k_{t}$)

又因為$\forall 1\le i<t,k_{i}+i-1<L_{b}$,即通過這$t$位1中最高的$t-1$位(即使是$L_{b}-i+1$)一定可以,同時也必然會刪除$a_{1},a_{2},...,a_{t-1}$

由於$[k_{t},L_{b}+1]$中第$t$個1(從高到低)必然是$k_{t}+1$位或第$k_{t}$位(也有可能都選),判定當第$k_{t}+1$位為0時能否刪除$a_{t},a_{t+1},...,a_{n}$,對結果分類討論:

1.若可以,即最低位可以為$k_{t}$,必然貪心選擇令第$[k_{t},L_{b}]$位為1並剩下$a_{t}-2^{k_{t}}$

2.若不可以,則第$[k_{t}+1,L_{b}+1]$位都必須填1,之後將$a_{1},a_{2},..,a_{t}$都刪除即可

考慮如何判定,可以再次呼叫本過程(遞迴),即在判定過程中順便求出最小解(若有解,否則返回無解),因此對於第1種情況直接就可以退出,第2種仍要遞迴下去

時間複雜度很玄學,遞迴次數大概是$o(L)$的

對於每一次內部,用線段樹來維護區間最大值,具體方法如下:

1.對於$i$可以通過插入/刪除一個數時,對之後的位置+1或-1來實現

2.對於每一個$a_{i}$,剩餘的一定是二進位制下的一個字尾,那麼將所有數每一個字尾(其實也只需要那一位上有1,否則跟上一個字尾相同)放在一起基數排序,至多為$o(\sum L)$個數

3.對於每一個$a_{i}$,要維護下一個1的位置(預處理);對於線段樹上,維護區間最大值,以及查詢第一個最大值並將小於等於其的位置暴力插入,支援單點插入或刪除

這樣的複雜度再多一個log,總複雜度即為$o((\sum L)\log \sum L)$

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define N 300005
  4 #define oo 0x3f3f3f3f
  5 #define L (k<<1)
  6 #define R (L+1)
  7 #define mid (l+r>>1)
  8 vector<int>v[N];
  9 int n,m,la,rk[N],bit[N],nex[N],top[N],ans[N],f[N<<2],tag[N<<2];
 10 char s[N];
 11 bool cmp(int x,int y){
 12     return rk[x]>rk[y];
 13 }
 14 void upd(int k,int x){
 15     tag[k]+=x;
 16     f[k]+=x;
 17 }
 18 void down(int k){
 19     upd(L,tag[k]);
 20     upd(R,tag[k]);
 21     tag[k]=0;
 22 }
 23 void build(int k,int l,int r){
 24     if (l==r){
 25         f[k]=bit[l]-oo;
 26         return;
 27     }
 28     build(L,l,mid);
 29     build(R,mid+1,r);
 30     f[k]=max(f[L],f[R]);
 31 }
 32 void update(int k,int l,int r,int x,int y,int z){
 33     if ((l>y)||(x>r))return;
 34     if ((x<=l)&&(r<=y)){
 35         upd(k,z);
 36         return;
 37     }
 38     down(k);
 39     update(L,l,mid,x,y,z);
 40     update(R,mid+1,r,x,y,z);
 41     f[k]=max(f[L],f[R]);
 42 }
 43 void query(int k,int l,int r,int x,vector<int>&v){
 44     if (f[k]<0)return;
 45     if (l==r){
 46         v.push_back(l);
 47         return;
 48     }
 49     down(k);
 50     query(L,l,mid,x,v);
 51     if (f[L]!=x)query(R,mid+1,r,x,v);
 52 }
 53 void add(int k){
 54     update(1,1,m,k,k,oo);
 55     if (k<m)update(1,1,m,k+1,m,1);
 56 }
 57 void del(int k){
 58     update(1,1,m,k,k,-oo);
 59     if (k<m)update(1,1,m,k+1,m,-1);
 60 }
 61 bool dfs(int mx){
 62     int lb=f[1];
 63     if (lb<0)return 1;
 64     if (lb>mx)return 0;
 65     vector<int>v;
 66     query(1,1,m,lb,v);
 67     for(int i=0;i<v.size();i++)del(v[i]);
 68     int t=v.back();
 69     if (nex[t])add(nex[t]);
 70     if (dfs(bit[t]-1)){
 71         for(int i=bit[t];i<=lb;i++)ans[i]=1;
 72         return 1;
 73     }
 74     if (nex[t])del(nex[t]);
 75     if ((lb+1<=mx)&&(dfs(bit[t]))){
 76         for(int i=bit[t];i<=lb;i++)ans[i+1]=1;
 77         return 1;
 78     }
 79     for(int i=0;i<v.size();i++)add(v[i]);
 80     return 0;
 81 }
 82 int main(){
 83     scanf("%d",&n);
 84     for(int i=1;i<=n;i++){
 85         scanf("%s",s);
 86         int l=strlen(s);
 87         la=max(la,l);
 88         for(int j=0;j<l;j++)
 89             if (s[j]=='1'){
 90                 m++;
 91                 v[l-j-1].push_back(i);
 92             }
 93     }
 94     for(int i=1;i<=n;i++)rk[i]=m+1;
 95     int mm=m;
 96     for(int i=0;i<la;i++){
 97         sort(v[i].begin(),v[i].end(),cmp);
 98         for(int j=0;j<v[i].size();j++){
 99             bit[mm]=i;
100             rk[v[i][j]]=mm;
101             nex[mm]=top[v[i][j]];
102             top[v[i][j]]=mm--;
103         }
104     }
105     build(1,1,m);
106     for(int i=1;i<=n;i++)add(top[i]);
107     dfs(oo);
108     bool flag=0;
109     for(int i=n+la;i>=0;i--)
110         if ((flag)||(ans[i])){
111             flag=1;
112             printf("%d",ans[i]);
113         }
114 } 
View Code