2026.1.3 题解

100+100+0=200,rk2。

A. xor on tree

到根链查询转成区间修改,就可以考虑直接线段树套 Trie,时间复杂度 \(O(n\log^2n)\)。但是空间太巨大了,考虑卡空间。发现我们可以定期重构,在此基础上,时间复杂度可以控制在 \(O(n\log^2n)\) 内,而空间可以 \(\times\log n\),即可通过本题。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=N*70;
int n,q,idx,wc[N],tot,cnt,kl=10000,lxl,rt[N],dfn[N],low[N];
int trp[N*32][2],nm[N*32],tr[M][2],num[M],rtk[50],tp,rtc[N];
vector<int>g[N];
inline void addc(int x,int y,int w,int v){
	x=(rtc[x]=++tot),y=rtc[y],nm[x]=nm[y]+v;
	for(int i=29,c;~i;i--){
		c=(w>>i)&1,trp[x][c^1]=trp[y][c^1];
		nm[x=(trp[x][c]=++tot)]=nm[y=trp[y][c]]+v;
	}
}
inline void add(int x,int w,int v){
	if(!rt[x]) rt[x]=++cnt;
	num[x=rt[x]]+=v;
	for(int i=29,c;~i;i--){
		c=(w>>i)&1;
		if(!tr[x][c]) tr[x][c]=++cnt;
		num[x=tr[x][c]]+=v;
	}
}
inline int sum(int c){
	int re=nm[trp[rtk[0]][c]];
	for(int i=1;i<=tp;i++) re+=num[tr[rtk[i]][c]];
	return re;
}
inline void trans(int c){
	rtk[0]=trp[rtk[0]][c];
	for(int i=1;i<=tp;i++) rtk[i]=tr[rtk[i]][c];
}
inline int get_ans(int w){
	int re=0;
	for(int i=29,c;~i;i--){
		c=((w>>i)&1)^1;
		if(sum(c)<=0) trans(c^1);
		else trans(c),re+=(1<<i);
	}
	return re;
}
inline void addrt(int x,int w,int v){
	for(;x<=n;x+=x&-x) add(x,w,v);
}
inline int answ(int x,int w){
	rtk[tp=0]=rtc[x],x=dfn[x];
	for(;x;x-=x&-x) rtk[++tp]=rt[x];
	return get_ans(w);
}
inline void dfs(int x,int fa){
	addc(x,fa,wc[x],1),dfn[x]=++idx;
	for(int y:g[x]) dfs(y,x);
	low[x]=idx;
}
inline void build(){
	for(int i=1;i<=tot;i++) trp[i][0]=trp[i][1]=nm[i]=0;
	for(int i=1;i<=cnt;i++) tr[i][0]=tr[i][1]=num[i]=0;
	for(int i=1;i<=n;i++) rt[i]=0;
	tot=cnt=idx=0,dfs(1,0);
}
signed main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>wc[i];
	for(int i=2,fa;i<=n;i++)
		cin>>fa,g[fa].push_back(i);
	build();
	while(q--){
		int opt,x,y;
		if(lxl==kl) lxl=0,build();
		cin>>opt>>x;
		if(opt==0){
			cin>>y,addrt(dfn[x],wc[x],-1),addrt(low[x]+1,wc[x],1);
			addrt(dfn[x],wc[x]=y,1),addrt(low[x]+1,y,-1),lxl++;
		}
		else cout<<answ(x,wc[x])<<"\n";
	}
	return 0;
} 

B. calc on lowbit

考虑找点规律。发现假如我们可以确定第 \(2^ik\) 和第 \(2^i(k+1)\) 个位置,就可以确定 \([2^ik,2^i(k+1))\) 这段区间的值。因为首先我们可以求出第 \(2^{i-1}(2k+1)\) 个位置的值,然后就可以将问题转化成两个形如 \(2^{i-1}t,2^{i-1}(t+1)\) 的子问题,归纳处理即可。我们也可以简单推理出这类区间 \(O(\log n)\) 级别的区间和公式。

计算一个位置的值可以用记忆化搜索做,我们又可以将 \([L,R]\) 这一段分成 \(\log\) 个满足上述要求的段,时间复杂度大致可以做到 \(O(\log^2n)\) 的程度。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int p=998244353,ivt=(p+1)>>1;
int L,R,ans;
inline int qpow(int x,int y){
	x%=p;
	int re=1;
	while(y){
		if(y&1) re=re*x%p;
		x=x*x%p,y>>=1;
	}
	return re;
}
unordered_map<int,int>dp;
inline int dps(int x){
	if(dp.count(x)) return dp[x];
	if(x==(x&-x)) return dp[x]=2;
	return dp[x]=((dps(x+(x&-x))+dps(x-(x&-x)))*ivt+1)%p;
}
inline int solve(int fs,int ed,int len){
	int re=fs,num=1;
	while(num<len) re=(re*2+(ed-fs+p)*ivt+num)%p,num<<=1;
	return re;
}
signed main(){
	freopen("calc.in","r",stdin);
	freopen("calc.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--){
		cin>>L>>R,ans=0;
		while(L+(L&-L)<=R+1)
			ans=(ans+solve(dps(L),dps(L+(L&-L)),L&-L))%p,L+=L&-L;
		for(int i=60;~i;i--) if(L+(1ll<<i)<=R+1)
			ans=(ans+solve(dps(L),dps(L+(1ll<<i)),1ll<<i))%p,L+=1ll<<i;
		cout<<ans<<"\n";
	}
	return 0;
}

C. color on board

令人蓝泵的网络流。

显然我们染色分三步:涂黑色、涂白色、涂单格。

考虑发现一些性质,比如:

一个格子不可能被横着或者竖着涂两次。
证明:我们只需要涂最后是黑色的段就可以了。

染色问题,这个数据范围考虑网络流。我们定义四个点代表他是否被横着涂黑过(\(bh\)),是否被纵着涂白过(\(wz\)),否是被横着涂白过(\(wh\)),否是被纵着涂黑过(\(bz\))。

考虑改变对最小割的理解方式。我们定义与 \(S\) 在同一边的点为 0,否则为 1。那么我们可以用一些公式来表达连边:

  • \((s,i,a):ia\)
  • \((i,t,a):(1-i)a\)
  • \((i,j,a):(1-i)ja\)

之所以可以这样表示,是因为这是这条边的贡献。

那我们就可以通过推公式的方法来连边。

首先我们考虑在每一个段的末尾处加上 b。以 \(bh\) 为例,公式即为:\(bh_{i,j}(1-bh_{i,j+1})a+bh_{i,j}b\)

然后考虑我们直接大力分讨不同颜色格子的贡献:

  1. 黑格子最后需要单点修改的贡献为 \(c(1-bh_{i,j})bz_{i,j}\)。由于涂了白就不能涂黑,所以还有限制贡献 \(+\infty(1-wh_{i,j})wz_{i,j}\)
  2. 由于刚才的性质,所以白格子最后需要单点修改的贡献为 \(c(1-wz_{i,j})bh_{i,j}+c(bz_{i,j})wh_{i,j}\)。由于不能涂两次黑,所以有限制贡献 \(+\infty(1-wz_{i,j})wh_{i,j}\)

时间复杂度远小于理论的 \(O(n^6)\)

#include<bits/stdc++.h>
#define bm(x,y) ((x)*m-m+(y))
using namespace std;
const int N=6405,M=40005;
int T,n,m,a,b,c,k,s,t;
int h[N],d[N],cc[N];
int to[M],w[M],nx[M];
int fs,ed,q[N];
char st[45][45];
inline void add(int x,int y,int v){
	nx[++k]=h[x],to[h[x]=k]=y,w[k]=v;
	nx[++k]=h[y],to[h[y]=k]=x,w[k]=0;
}
inline int bfs(){
	for(int i=s;i<=t;i++) cc[i]=-1;
	cc[q[fs=ed=1]=s]=0,d[s]=h[s];
	while(fs<=ed){
		int x=q[fs++];
		for(int i=h[x];i;i=nx[i])
			if(w[i]&&cc[to[i]]<0){
				d[to[i]]=h[to[i]];
				cc[q[++ed]=to[i]]=cc[x]+1;
				if(to[i]==t) return 1;
			}
	}
	return 0;
}
inline int dfs(int x,int ans){
	if(x==t) return ans;
	int sum=ans;
	for(int i=d[x];i&&sum;i=nx[i]){
		d[x]=i;
		if(w[i]<1||cc[to[i]]!=cc[x]+1) continue;
		int ad=dfs(to[i],min(sum,w[i]));
		sum-=ad,w[i]-=ad,w[i^1]+=ad;
	}
	return ans-sum;
}
inline int dinic(){
	int re=0;
	while(bfs()) re+=dfs(s,1e9);
	return re;
}
inline void solve(){
	cin>>n>>m>>a>>b>>c;
	k=1,s=0,t=n*m*4+1;
	for(int i=s;i<=t;i++) h[i]=0;
	for(int i=1;i<=n;i++) cin>>(st[i]+1);
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
		add((j==m?s:bm(i,j+1)),bm(i,j),b);
		add(bm(i,j)+n*m,(i==n?t:bm(i+1,j)+n*m),b);
		add(bm(i,j)+n*m*2,(j==m?t:bm(i,j+1)+n*m*2),b);
		add((i==n?s:bm(i+1,j)+n*m*3),bm(i,j)+n*m*3,b);
		add(s,bm(i,j),a),add(bm(i,j)+n*m,t,a);
		add(s,bm(i,j)+n*m*3,a),add(bm(i,j)+n*m*2,t,a);
		if(st[i][j]=='#'){
			w[k-1]=w[k-3]=1e9;
			add(bm(i,j),bm(i,j)+n*m,c);
		}
		else{
			add(bm(i,j)+n*m*3,bm(i,j),c);
			add(bm(i,j)+n*m,bm(i,j)+n*m*2,c);
			add(bm(i,j)+n*m,bm(i,j),1e9);
		}
	}
	cout<<dinic()<<"\n";
}
int main(){
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--) solve();
	return 0;
}
posted @ 2026-01-10 09:13  white_tiger  阅读(6)  评论(0)    收藏  举报