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;
}
posted @ 2025-04-23 21:17  zxh923  阅读(47)  评论(0)    收藏  举报