LibreOJ 2850 无进位加法
首先这个操作和题目名字一样,就是无进位加法。
首先我们考虑怎么判断一个 \(\sum b_i\) 是否合法。
我们先将所有数降序排序,然后每次找出当前的最大数,假设最大数有 \(x\) 位,当前要确定答案的第 \(y\) 位,我们分情况讨论一下:
-
\(x>y\),那么直接无解。
-
\(x=y\),消掉最大数的最高位后继续判断。
-
\(x<y\),消掉最大数后继续做。
到了这里,我们有一个自然的想法,判断每一位放不放 \(1\),然后进行判断。但这样复杂度会炸掉,我们考虑优化。
考虑对于一个降序排序的 \(a\),设 \(a_i\) 的最高位为 \(t_i\),假设 \(\sum b_i\) 的最高位为 \(q\),考虑会有什么性质:
-
当 \(q<\max(t_i+i-1)\) 时,一定无解。因为此时前 \(i-1\) 个数耗掉了至少 \(i-1\) 位,此时这一位一定是上面提到的第一种情况。
-
当 \(q>\max(t_i+i-1)\) 时,一定有解。因为此时当前位遇到每一个数的时候,都是上面提到的第三种情况,所以就能直接消掉,即一定有解。
发现我们只需要判断 \(\max(t_i+i-1)\) 是否合法即可,这个限制是相当严的,可以考虑一下怎么实现了。
我们可以同时进行判断和构造。例如当前最高位大于当前填的位置,显然就需要回溯了。那么我们现在有一个问题,如何快速找出 \(\max(t_i+i-1)\)。
我们可以使用线段树,具体原理比较复杂,可以看代码理解。
那么我们现在找到了 \(\max(t_i+i-1)\),直接尝试把这一位作为最高位然后递归下去即可,如果发现不合法则直接将第 \(\max(t_i+i-1)+1\) 位作为最高位即可。
考虑这个东西的时间复杂度,如果我们不需要回溯,复杂度显然为 \(O(n\log (\sum L))\)。否则每个串至多回溯一次,即至多回溯 \(\sum L\),所以时间复杂度 \(O((n+\sum L)\log (\sum L))\)。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 300005
#define pii pair<int,int>
#define x first
#define y second
#define mod 998244353
#define inf 1e9
using namespace std;
int T=1,n,m,tot,mx,sum[N],las[N],id[N],res[N];
pii all[N];
vector<int>e[N],rk[N];
vector<pii>p[N];
set<int>s;
struct sgt{
pii tr[N<<2];
int lzy[N<<2];
void pushup(int u){
tr[u]=max(tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
if(l==r){
tr[u]={e[all[l].x][all[l].y]+sum[l]-1,l};
if(all[l].y==e[all[l].x].size()-1)s.insert(l);
else tr[u].x-=inf;
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void maketag(int u,int v){
tr[u].x+=v;
lzy[u]+=v;
}
void pushdown(int u){
if(!lzy[u])return;
maketag(u<<1,lzy[u]);
maketag(u<<1|1,lzy[u]);
lzy[u]=0;
}
void modify(int u,int l,int r,int L,int R,int v){
if(l>=L&&r<=R){
maketag(u,v);
return;
}
pushdown(u);
int mid=l+r>>1;
if(L<=mid)modify(u<<1,l,mid,L,R,v);
if(R>mid)modify(u<<1|1,mid+1,r,L,R,v);
pushup(u);
}
}sgt;
bool dfs(int cur){
if(sgt.tr[1].x<0)return 1;
vector<int>can;
int now=sgt.tr[1].x;
int u=sgt.tr[1].y;
int cnt=0;
if(now>=cur)return 0;
while(!s.empty()&&(*s.rbegin())>=u){
cnt++;
int ao=*s.rbegin();
sgt.modify(1,1,tot,1,ao,-1);
sgt.modify(1,1,tot,ao,ao,-inf);
s.erase(ao);
can.push_back(ao);
}
cnt--;
for(int i=0;i<cnt;i++){
res[now-i]=1;
}
int p=all[u].x,q=all[u].y;
if(q>0){
int v=rk[p][q-1];
sgt.modify(1,1,tot,1,v,1);
sgt.modify(1,1,tot,v,v,inf);
s.insert(v);
}
mx=max(mx,now);
if(dfs(now-cnt)){
res[now-cnt]=1;
return 1;
}
if(q>0){
int v=rk[p][q-1];
sgt.modify(1,1,tot,1,v,-1);
sgt.modify(1,1,tot,v,v,-inf);
s.erase(v);
}
if(now+1>=cur){
for(int i=0;i<cnt;i++){
res[now-i]=0;
}
while(!can.empty()){
int ao=can.back();
sgt.modify(1,1,tot,1,ao,1);
sgt.modify(1,1,tot,ao,ao,inf);
s.insert(ao);
can.pop_back();
}
return 0;
}
res[now+1]=1;
mx=max(mx,now+1);
return now+1<cur&&dfs(now+1-cnt);
}
void solve(int cs){
cin>>n;
for(int i=1;i<=n;i++){
string s;
cin>>s;
for(int j=0;j<s.size();j++){
if(s[j]=='1'){
e[i].push_back(s.size()-j-1);
}
}
int siz=e[i].size();
rk[i].resize(siz);
reverse(e[i].begin(),e[i].end());
for(int j=0;j<siz;j++){
p[e[i][j]].push_back({i,j});
}
m=max(m,(int)s.size());
}
for(int i=0;i<m;i++){
int siz=p[i].size();
for(int j=0;j<siz;j++){
id[j]=j;
las[j]=p[i][j].y?rk[p[i][j].x][p[i][j].y-1]:0ll;
}
sort(id,id+siz,[&](int a,int b){
return las[a]==las[b]?p[i][a].x<p[i][b].x:las[a]<las[b];
});
for(int j=0;j<siz;j++){
rk[p[i][id[j]].x][p[i][id[j]].y]=++tot;
all[tot]=p[i][id[j]];
}
}
for(int i=tot;i;i--){
sum[i]=sum[i+1]+(all[i].y==e[all[i].x].size()-1);
}
sgt.build(1,1,tot);
dfs(inf);
for(int i=mx;~i;i--){
cout<<res[i];
}
cout<<'\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}