CTT 2025 补题小记

Day 1

A. 异形工厂

首先可以把操作看成交换 \(s_i,s_{i+1}\) 或交换 \(s_i,s_{i+2}\)

如果 \(\tt 01\) 总量都不同肯定直接倒闭了。如果一样,那么基本上是让一个 \(\tt 0/1\) 和一个 \(\tt 1/0\) 湮灭。可以证明*的是,对于这样的一次湮灭,其代价等价于在 \(\tt 0/1\) 位置放一个棋子,通过操作操作到 \(\tt 1/0\) 位置的代价。同时等价于,把 \(\tt 0/1\)\(\tt 1/0\) 的点全部抽出来,总点数减最大匹配数,即最大独立集。

因此,令 \(\displaystyle c_i=\sum_{j=1}^is_j-t_j\),将 \(c\) 按正负分成两个非负绝对值数组 \(c_+\)\(c_-\),代表只考虑正的/负的 \(c_i\),答案就是两个数组的最大权独立集之和。

考虑区间怎么做,相当于查询 \(c'[l:r]\) 的答案,其中 \(c_i'\leftarrow c_i-c_{l-1}\)。我们考察对于 \(-n\leq k\leq n\)\(c_i+k\) 的最大权独立集的形态。令 \(\tt 0/1\) 表示选了或没选。可以发现,所有负权点都是 \(\tt 0\)。剩下的正权点会在某个时刻因为 \(k\) 的增加从 \(\tt 01010\) 变成 \(\tt 10101\),而这个 \(\tt 10101\) 又会和旁边的两个 \(\tt 01010\) 组成更长的 \(\tt 010101010101010\)。由于一些边界原因,可能会变成 \(\tt 1010/0101/10101\)。考虑维护这 \(4\) 种区间,并且记录 \(\tt 01010\) 串的更新时间,扔进堆里进行及时的更新。

由于每次更新都会缩掉一个间隔,所以更新次数是 \(O(n)\) 的,同时,因为 \(c_r-c_{l-1}=c_{l-1}-c_{l-1}=0\),所以查询都是整段的,直接使用树状数组维护段的权值即可。时间复杂度 \(O((n+q)\log n)\)

证明*:

对于目标的 \(\tt 0/1\)\(\tt 1/0\) 中间没有变成 \(t_i\)\(s_i\),我们姑且认为它们的现阶段目标 \(t_i'=s_i\),否则 \(t'_i=t_i\)

方便起见,令 \(\tt 0/1\) 在左边且位置为 \(l\)\(\tt 1/0\) 在右边且位置为 \(r\)。我们要证明的是,存在一个子序列 \(p_1=l\leq \ldots \leq p_k=r\),使得 \(p_i\)\(p_{i+1}\) 是可以通过一次合法操作交换的,且可以花 \(k-1\) 次操作使得 \(s_{p_i}=t'_{p_i}\)

上面的命题相当于给定一个开头是 \(\tt 0\) 结尾是 \(\tt 1\) 中间任意的一个串,要求 flip 头尾两个字符,其他不动,可以执行的操作是 swap 相邻的两个字符。

\(\tt 1\) 看成箱子,\(0\) 看成空地,并强制所有箱子的相对顺序不变,那么这就变成 AGC004F 的一个特殊情况了。根据结论,最少交换次数为所有子树原有箱子总数和目标箱子总数差的绝对值之和。而这条链所有子树原有箱子总数和目标箱子总数差恰好为 \(-1\),因此答案为 \(k-1\)

#include<bits/stdc++.h>

using namespace std;

using ll=long long;
const int N=5e5+9;

ll su[N][2],ans[N];
char s[N],t[N];
int c[N],ddl[N],ql[N],qr[N],n,q;
vector<int> qry[N<<1];

struct Fenw{
	ll tr[N];
	inline void Add(int x,ll k){while(x<=n) tr[x]+=k,x+=x&-x;}
	inline ll Ask(int x){ll sum=0;while(x) sum+=tr[x],x&=x-1;return sum;}
	inline ll Ask(int l,int r){return Ask(r)-Ask(l-1);}
	inline void Clear(){for(int i=1;i<=n;i++) tr[i]=0;}
}k,b;

multiset<array<int,2>> seg[2][2];
priority_queue<array<int,2>> cnd,iac;
inline void Insert(int l,int r,int lp,int rp){
	seg[lp][rp].insert({l,r});
	k.Add(l,(r-l+1+lp+rp)/2);
	b.Add(l,su[r-!rp][r-!rp&1]-su[l+!lp-1][r-!rp&1]);
	if(!lp&&!rp){
		ll f=su[r][r&1]-su[l-1][r&1],g=su[r][~r&1]-su[l-1][~r&1];
		if(g-f+1<=n) cnd.push({-(ddl[l]=g-f+1),l});
	}
}
inline void Erase(int l,int r,int lp,int rp){
	seg[lp][rp].erase({l,r});
	k.Add(l,-(r-l+1+lp+rp)/2);
	b.Add(l,-(su[r-!rp][r-!rp&1]-su[l+!lp-1][r-!rp&1]));
	ddl[l]=-1e9;
}
inline void Activate(int pos){
	int l=pos,r=pos,lp=0,rp=0;
	for(int p:{0,1}){
		auto it=seg[p][1].lower_bound({pos,0});
		if(it==seg[p][1].begin()) continue ;
		it--;
		if((*it)[1]!=pos-1) continue ;
		lp=p;
		l=(*it)[0];
		Erase((*it)[0],(*it)[1],p,1);
		break ;
	}
	for(int p:{0,1}){
		auto it=seg[1][p].upper_bound({pos,0});
		if(it==seg[1][p].end()) continue ;
		if((*it)[0]!=pos+1) continue ;
		rp=p;
		r=(*it)[1];
		Erase((*it)[0],(*it)[1],1,p);
		break ;
	}
	Insert(l,r,lp,rp);
}
inline void Update(int l){
	int r=-1,lp=1,rp=1;
	for(int p:{0,1}){
		for(int q:{0,1}){
			auto it=seg[p][q].lower_bound({l,0});
			if(it==seg[p][q].end()||(*it)[0]!=l) continue ;
			r=(*it)[1];
			break ;
		}
	}
	Erase(l,r,0,0);
	for(int p:{0,1}){
		auto it=seg[p][0].lower_bound({l,0});
		if(it==seg[p][0].begin()) continue ;
		it--;
		if((*it)[1]!=l-1) continue ;
		lp=p;
		l=(*it)[0];
		Erase((*it)[0],(*it)[1],p,0);
		break ;
	}
	for(int p:{0,1}){
		auto it=seg[0][p].upper_bound({r,0});
		if(it==seg[0][p].end()) continue ;
		if((*it)[0]!=r+1) continue ;
		rp=p;
		r=(*it)[1];
		Erase((*it)[0],(*it)[1],0,p);
		break ;
	}
	Insert(l,r,lp,rp);
}

signed main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>s[i];
	for(int i=1;i<=n;i++) cin>>t[i];
	
	for(int i=1;i<=n;i++) c[i]=c[i-1]+(s[i]-t[i]);
	for(int i=1;i<=n;i++){
		for(int k:{0,1}) su[i][k]=su[i-1][k];
		su[i][i&1]+=c[i];
	}

	for(int i=1;i<=q;i++){
		cin>>ql[i]>>qr[i];
		if(qr[i]-ql[i]+1<3) ans[i]=-int(string(s+ql[i],s+qr[i]+1)!=string(t+ql[i],t+qr[i]+1));
		else if(c[ql[i]-1]==c[qr[i]]) qry[-c[ql[i]-1]+N].push_back(i);
		else ans[i]=-1;
	}
	
	for(int o:{0,1}){
		for(int i=1;i<=n;i++) iac.push({c[i]-1,i});
		for(int i=-n;i<=n;i++){
			while(iac.size()&&-iac.top()[0]<=i){
				int j=iac.top()[1];
				iac.pop();
				Activate(j);
			}
			while(cnd.size()&&-cnd.top()[0]<=i){
				int j=cnd.top()[1],x=-cnd.top()[0];
				cnd.pop();
				if(ddl[j]!=x) continue ;
				Update(j);
			}
			for(int j:qry[i+N]){
				ans[j]+=k.Ask(ql[j],qr[j])*i+b.Ask(ql[j],qr[j]);
			}
		}
		k.Clear();
		b.Clear();
		for(int p:{0,1}) for(int q:{0,1}) seg[p][q].clear();
		while(cnd.size()) cnd.pop();
		while(iac.size()) iac.pop();
		for(int i=1;i<=n;i++){
			c[i]=-c[i];
			for(int k:{0,1}) su[i][k]=-su[i][k];
			qry[i+N].swap(qry[-i+N]);
		}
	}

	for(int i=1;i<=q;i++) cout<<ans[i]<<endl;

	return 0;
}

B. 字符串问题

先对 \(f\) 做 Dirichlet 差分,即令 \(g=f*\mu\)。那么现在贡献就是所有整周期 \(L\)\(g(\dfrac nL)\) 的和。考虑求出所有 Runs,那么所有对于 Runs \((l,r,p)\),所有 \(p|q\)\(2q\leq r-l+1\)\(q\) 都可以作为 \([i-kq+1,i]\) 的整周期,其中 \(2\leq k\leq \dfrac{r-l+1}q,l+kq-1\leq i\leq r\)。相当于对 \([l+kq-1,r]\) 区间加 \(g(q)\),可以差分 \(O(1)\) 做。由于对于任意 \(q\),包含其作为整周期的 \(r-l+1\) 之和不会超过 \(n\),所以 \(k\)\(O(\dfrac nq)\) 的,枚举 \(k\) 的复杂度就是调和级数的。加上求 Runs 总体就是 \(O(n\log n)\) 的。

#include<bits/stdc++.h>

using namespace std;

#define endl '\n'
using ll=long long;
const int N=1e6+9;
const int lgN=2e1;
const int mod=998244353;

inline void AddAs(int &x,int y){if((x+=y)>=mod) x-=mod;}
inline void SubAs(int &x,int y){if((x-=y)<0) x+=mod;}
inline void MulAs(int &x,int y){x=1ll*x*y%mod;}
inline int Add(int x,int y){if((x+=y)>=mod) x-=mod;return x;}
inline int Sub(int x,int y){if((x-=y)<0) x+=mod;return x;}
inline int Mul(int x,int y){return 1ll*x*y%mod;}
inline int QPow(int x,int y){
	int res=1;
	while(y){
		if(y&1) MulAs(res,x);
		MulAs(x,x);
		y>>=1;
	}
	return res;
}
#define Inv(x) QPow(x,mod-2)

struct SufArr{
	string s;
	int sa[N],rk[N<<1],lsa[N],lrk[N<<1],cnt[N],h[N],lg[N],mn[lgN][N],n;

	inline void Init(string str){
		s=" "+str;
		n=str.size();

		for(int i=1;i<=n;i++) cnt[rk[i]=s[i]]++;
		for(int i=1;i<128;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
		for(int i=0;i<128;i++) cnt[i]=0;

		int m=0;
		for(int i=1;i<=n;i++) lrk[i]=rk[i];
		for(int i=1;i<=n;i++){
			if(lrk[sa[i]]!=lrk[sa[i-1]]) m++;
			rk[sa[i]]=m;
		}

		for(int k=1;k<=n&&m<n;k<<=1){
			int t=0;
			for(int i=n-k+1;i<=n;i++) lsa[++t]=i;
			for(int i=1;i<=n;i++) if(sa[i]>k) lsa[++t]=sa[i]-k;
			
			for(int i=1;i<=n;i++) cnt[rk[lsa[i]]]++;
			for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
			for(int i=n;i>=1;i--) sa[cnt[rk[lsa[i]]]--]=lsa[i];
			for(int i=0;i<=m;i++) cnt[i]=0;

			m=0;
			for(int i=1;i<=n;i++) lrk[i]=rk[i];
			for(int i=1;i<=n;i++){
				if(lrk[sa[i]]!=lrk[sa[i-1]]||lrk[sa[i]+k]!=lrk[sa[i-1]+k]) m++;
				rk[sa[i]]=m;
			}
		}

		for(int i=1,k=0;i<=n;i++){
			if(k) k--;
			while(s[i+k]==s[sa[rk[i]-1]+k]) k++;
			h[rk[i]]=k;
		}

		for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
		for(int i=1;i<=n;i++) mn[0][i]=h[i];
		for(int k=1;k<=lg[n];k++){
			for(int i=1;i<=n-(1<<k)+1;i++){
				mn[k][i]=min(mn[k-1][i],mn[k-1][i+(1<<k-1)]);
			}
		}
	}
	inline int Min(int l,int r){
		int k=lg[r-l+1];
		return min(mn[k][l],mn[k][r-(1<<k)+1]);
	}
	inline int LCP(int i,int j){
		if(i==j) return n-i+1;
		else return Min(min(rk[i],rk[j])+1,max(rk[i],rk[j]));
	}
}arr,rra;

string s;
int f[N],g[N],d[N],n;
vector<array<int,3>> runs;

int ntp[N],pri[N],mu[N],pcnt;
inline void Init(int lim){
	mu[1]=1;
	for(int i=2;i<=lim;i++){
		if(!ntp[i]) pri[++pcnt]=i,mu[i]=mod-1;
		for(int j=1;j<=pcnt&&i*pri[j]<=lim;j++){
			ntp[i*pri[j]]=1;
			if(i%pri[j]) mu[i*pri[j]]=Sub(0,mu[i]);
			else{
				mu[i*pri[j]]=0;
				break ;
			}
		}
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);

	cin>>n>>s;
	for(int i=1;i<=n;i++) cin>>f[i];

	int tmp=f[1];
	for(int i=1;i<=n;i++) SubAs(f[i],tmp);
	Init(n);
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j+=i) AddAs(g[j],Mul(f[i],mu[j/i]));
	}

	arr.Init(string(s.begin(),s.end()));
	rra.Init(string(s.rbegin(),s.rend()));
	s=" "+s;

	vector<int> stk;
	for(int c:{0,1}){
		for(int i=n;i>=1;i--){
			while(stk.size()&&((arr.rk[i]<arr.rk[stk.back()])^c)) stk.pop_back();
			if(stk.size()){
				int j=stk.back(),p=j-i;
				int l=i-rra.LCP(n-i+1,n-j+1)+1,r=j+arr.LCP(i,j)-1;
				if(r-l+1>=2*p&&arr.LCP(l,l+p)==r-l+1-p) runs.push_back({l,r,p});
			}
			stk.push_back(i);
		}
		stk.clear();
	}

	sort(runs.begin(),runs.end());
	runs.erase(unique(runs.begin(),runs.end()),runs.end());

	for(auto t:runs){
		int l=t[0],r=t[1],p=t[2];
		for(int q=p;q<=r-l+1;q+=p){
			for(int k=2;k*q<=r-l+1;k++){
				AddAs(d[l+k*q-1],g[k]);
				SubAs(d[r+1],g[k]);
			}
		}
	}
	for(int i=1;i<=n;i++) AddAs(d[i],d[i-1]);
	for(int i=1;i<=n;i++) AddAs(d[i],Mul(i,tmp));

	for(int i=1;i<=n;i++) cout<<d[i]<<' ';cout<<endl;

	return 0;
}

C. 三选二

考虑根号分治:

  • 对于 \(a_i\gt L\),其涂黑的点总数不超过 \(\dfrac n{L}\) 的,那么可以找到每个点左右最近的两个点,用 \(O(\dfrac nL)\) 的代价修正答案。
  • 对于 \(a_i\leq L\),找出所有这样的 \(a_i\),那么 \(\displaystyle \prod_{a_i\leq L} a_i\) 就是一个周期。对于某个特定的 \(j\),其涂黑的点个数在这个周期中共有 \(\displaystyle \dfrac1{a_j}\prod_{a_t\leq L} a_i\) 个。所以可以用 \(\displaystyle O(\sum_{a_j\leq L}\dfrac1{a_j}\prod_{a_t\leq L} a_i)\leq O(L^{-1+\sum [a_i\leq L]})\) 的代价求解答案。

\(L=\sqrt[3] n\) 取到复杂度 \(O(n^{\frac 23})\)

#include<bits/stdc++.h>

using namespace std;

using ll=long long;
const int L=3e4;
const int mod=998244353;

inline void AddAs(int &x,int y){if((x+=y)>=mod) x-=mod;}
inline void SubAs(int &x,int y){if((x-=y)<0) x+=mod;}
inline void MulAs(int &x,int y){x=1ll*x*y%mod;}
inline int Add(int x,int y){if((x+=y)>=mod) x-=mod;return x;}
inline int Sub(int x,int y){if((x-=y)<0) x+=mod;return x;}
inline int Mul(int x,int y){return 1ll*x*y%mod;}
inline int QPow(int x,int y){
	int res=1;
	while(y){
		if(y&1) MulAs(res,x);
		MulAs(x,x);
		y>>=1;
	}
	return res;
}
inline int Inv(int x){return QPow(x,mod-2);}

ll a[3],b[3],n;

inline ll Prev(ll x,ll m,ll r){
	ll t=x%m;
	if(t>=r) return x-t+r;
	else return x-t-m+r;
}
inline ll Next(ll x,ll m,ll r){
	ll t=x%m;
	if(t<=r) return x-t+r;
	else return x-t+m+r;
}

const int inv2=mod+1>>1;
inline int F(ll x){return x%=mod,Mul(x,Add(x,1));}
inline int iF(ll x){return Mul(x,Add(x,1));}
inline void LLL(){
	cerr<<"LLL"<<endl;
	ll it[3];
	for(int k:{0,1,2}) it[k]=b[k];
	
	int ans=0;
	ll lst=-1;
	while(true){
		int i=min_element(it,it+3)-it;
		if(it[i]>n){
			AddAs(ans,F(n-lst-1));
			break ;
		}
		AddAs(ans,F(it[i]-lst-1));
		lst=it[i];
		it[i]+=a[i];
	}

	cout<<Mul(ans,inv2)<<endl;
}
inline void SSS(){
	cerr<<"SSS"<<endl;
	ll it[3],lim=1;
	for(int k:{0,1,2}) it[k]=b[k];
	for(int k:{0,1,2}) lim=lim/__gcd(a[k],lim)*a[k];

	if(n<=lim) return LLL();
	
	int ans=0;
	ll lst=*min_element(b,b+3);
	while(true){
		int i=min_element(it,it+3)-it;
		if(it[i]>lim) break ;
		AddAs(ans,F(it[i]-lst-1));
		lst=it[i];
		it[i]+=a[i];
	}

	ll d=n/lim%mod;
	MulAs(ans,d);
	AddAs(ans,Mul(Sub(d,1),F(lim+*min_element(b,b+3)-lst-1)));
	AddAs(ans,F(*min_element(b,b+3)));

	lst+=lim*(n/lim-1);
	for(int k:{0,1,2}) it[k]+=lim*(n/lim-1);
	while(true){
		int i=min_element(it,it+3)-it;
		if(it[i]>n){
			AddAs(ans,F(n-lst-1));
			break ;
		}
		AddAs(ans,F(it[i]-lst-1));
		lst=it[i];
		it[i]+=a[i];
	}

	cout<<Mul(ans,inv2)<<endl;
}
inline void SSL(){
	cerr<<"SSL"<<endl;
	ll it[3],lim=1;
	for(int k:{0,1}) it[k]=b[k];
	for(int k:{0,1}) lim=lim/__gcd(a[k],lim)*a[k];

	if(n<=lim) return LLL();
	
	int ans=0;
	ll lst=*min_element(b,b+2);
	while(true){
		int i=min_element(it,it+2)-it;
		if(it[i]>lim) break ;
		AddAs(ans,iF(it[i]-lst-1));
		lst=it[i];
		it[i]+=a[i];
	}

	ll d=n/lim%mod;
	MulAs(ans,d);
	AddAs(ans,Mul(Sub(d,1),iF(lim+*min_element(b,b+2)-lst-1)));
	AddAs(ans,iF(*min_element(b,b+2)));

	lst+=lim*(n/lim-1);
	for(int k:{0,1}) it[k]+=lim*(n/lim-1);
	while(true){
		int i=min_element(it,it+2)-it;
		if(it[i]>n){
			AddAs(ans,iF(n-lst-1));
			break ;
		}
		AddAs(ans,iF(it[i]-lst-1));
		lst=it[i];
		it[i]+=a[i];
	}

	for(ll x=b[2];x<n;x+=a[2]){
		ll l=max(-1ll,x-a[2]),r=n;
		for(int k:{0,1}){
			l=max(l,Prev(x,a[k],b[k]));
			r=min(r,Next(x,a[k],b[k]));
		}
		SubAs(ans,iF(r-l-1));
		AddAs(ans,iF(r-x-1));
		AddAs(ans,iF(x-l-1));
	}

	cout<<Mul(ans,inv2)<<endl;
}
inline void SLL(){
	cerr<<"SLL"<<endl;
	int ans=0;
	if(n<=b[0]) ans=F(n);
	else{
		ll d=(n-b[0])/a[0]%mod;
		AddAs(ans,F(b[0]));
		AddAs(ans,Mul(d,F(a[0]-1)));
		AddAs(ans,F(n-Prev(n,a[0],b[0])-1));
	}

	ll it[3];
	for(int k:{0,1,2}) it[k]=b[k];
	
	ll lst=-1;
	while(true){
		int i=min_element(it+1,it+3)-it;
		if(it[i]>n) break ;

		ll l=max(-1ll,lst),r=n;
		l=max(l,Prev(it[i],a[0],b[0]));
		r=min(r,Next(it[i],a[0],b[0]));
		SubAs(ans,iF(r-l-1));
		AddAs(ans,iF(r-it[i]-1));
		AddAs(ans,iF(it[i]-l-1));

		lst=it[i];
		it[i]+=a[i];
	}

	cout<<Mul(ans,inv2)<<endl;
}

signed main(){
	cin>>n;
	for(int i:{0,1,2}) cin>>a[i]>>b[i];

	for(int i=0;i<3;i++){
		for(int j=i;j>0;j--){
			if(a[j]<a[j-1]){
				swap(a[j],a[j-1]);
				swap(b[j],b[j-1]);
			}else break ;
		}
	}

	if(a[0]>L) LLL();
	else if(a[1]>L) SLL();
	else if(a[2]>L) SSL();
	else SSS();

	return 0;
}

Day 2

A. 深红

这我哪会啊。

B. 小丑大师的荣耀

首先建图,边集 \(E=\{(x_i,y_i)|1\leq i\leq n\}\)

手玩发现如果一个连通块 \(S=\{V',E'\}\) 至少有 \(|E'|\) 条边,则这个连通块里的值一定可以被全部表示出来,否则必然存在一个数无法被表示出来,并且这个数从 \(V'\) 中任选。

换言之,如果存在一棵树,使得 \([\min V',\max V']\) 是区间 \([l,r]\) 的子区间,那么 \([l,r]\) 就无法被表示出来。

考虑在 \(\min V'\) 处维护 \(\max V'\) 的值,那么不合法区间的面积(右下部分)就可以表示成前缀最大值的和。左上部分则是 \(l=r\) 直线上方的整点,可以通过简单的计算直接扣掉。直接在线段树分治的同时维护楼房重建线段树是 \(O(n\log^3 n)\) 的。

发现事实上每次修改其实只对 \(O(1)\) 个连通块有影响,因此考虑在线段树分治的时候找出 \(t\) 时刻和 \(t-1\) 时刻 \(\max V'\) 不同的位置,记录下来,在外面再跑楼房重建线段树。时间复杂度 \(O(n\log^2 n)\)

#include<bits/stdc++.h>

using namespace std;

#define endl '\n'
using ll=long long;
const int N=2e5+9;

int op[N],m,q;
vector<int> ar[N];

vector<pair<int&,int>> rcll;
int fa[N],siz[N],pl[N],pr[N],ext[N],now[N];
inline void Init(){for(int i=1;i<=m;i++) fa[i]=pl[i]=pr[i]=i,siz[i]=1,ext[i]=0;}
inline int Find(int x){return fa[x]==x?x:Find(fa[x]);}
inline void Record(int &x){rcll.push_back({x,x});}
inline void Recall(int prv){
	while(rcll.size()>prv){
		rcll.back().first=rcll.back().second;
		rcll.pop_back();
	}
}
vector<int> tmp;
vector<pair<int,int>> ncll;
inline void Necord(int i){ncll.push_back({i,now[i]});}
inline void Necall(int prv){
	while(ncll.size()>prv){
		tmp.push_back(ncll.back().first);
		now[ncll.back().first]=ncll.back().second;
		ncll.pop_back();
	}
}
inline void Merge(int x,int y){
	x=Find(x),y=Find(y);
	if(x==y){
		if(!ext[x]){
			Record(ext[x]),Record(pl[x]),Necord(pr[x]);
			tmp.push_back(pr[x]);
			ext[x]=1,pl[x]=0,now[pr[x]]=pl[x];
		}
	}else{
		if(siz[x]<siz[y]) swap(x,y);
		Record(fa[y]),Record(siz[x]),Record(pl[x]),Record(pr[x]),Record(ext[x]);
		Necord(pr[x]),Necord(pr[y]);
		tmp.push_back(pr[x]);
		tmp.push_back(pr[y]);
		now[pr[x]]=now[pr[y]]=0;
		fa[y]=x;
		siz[x]+=siz[y];
		pl[x]=min(pl[x],pl[y]);
		pr[x]=max(pr[x],pr[y]);
		ext[x]|=ext[y];
		if(ext[x]) pl[x]=0;
		now[pr[x]]=pl[x];
	}
}

vector<array<int,2>> e[N<<2],upd[N];
int eu[N],ev[N],lst[N],id[N],st[N],ed[N],n;
inline void Insert(int x,int L,int R,int l,int r,int u,int v){
	if(l<=L&&R<=r) return e[x].push_back({u,v});
	int mid=L+R>>1;
	if(l<=mid) Insert(x<<1,L,mid,l,r,u,v);
	if(r>mid) Insert(x<<1|1,mid+1,R,l,r,u,v);
}
inline void Conquer(int x,int L,int R){
	int dprv=rcll.size(),nprv=ncll.size();
	for(auto p:e[x]) Merge(p[0],p[1]);
	if(L==R){
		for(int i:tmp){
			if(lst[i]==now[i]) continue ;
			upd[L].push_back({i,now[i]});
			lst[i]=now[i];
		}
		tmp.clear();
	}else{
		int mid=L+R>>1;
		Conquer(x<<1,L,mid);
		Conquer(x<<1|1,mid+1,R);
	}
	Recall(dprv),Necall(nprv);
}

struct Node{
	int l,r;
	ll mx,sum;
	inline int Len(){return r-l+1;}
}tr[N<<2];

inline ll Calc(int x,ll k){
	if(k>=tr[x].mx) return k*tr[x].Len();
	if(tr[x].l==tr[x].r) return tr[x].mx;
	if(k<tr[x<<1].mx) return Calc(x<<1,k)+tr[x].sum-tr[x<<1].sum;
	else return k*tr[x<<1].Len()+Calc(x<<1|1,k);
}
inline void PushUp(int x){
	tr[x].mx=max(tr[x<<1].mx,tr[x<<1|1].mx);
	tr[x].sum=tr[x<<1].sum+Calc(x<<1|1,tr[x<<1].mx);
}

inline void Build(int x,int l,int r){
	tr[x].l=l,tr[x].r=r;
	if(tr[x].l==tr[x].r) return tr[x].mx=tr[x].sum=l,void();
	int mid=tr[x].l+tr[x].r>>1;
	Build(x<<1,l,mid),Build(x<<1|1,mid+1,r);
	PushUp(x);
}
inline void Modify(int x,int pos,ll k){
	if(tr[x].l==tr[x].r) return tr[x].mx=tr[x].sum=k,void();
	int mid=tr[x].l+tr[x].r>>1;
	if(pos<=mid) Modify(x<<1,pos,k);
	else Modify(x<<1|1,pos,k);
	PushUp(x);
}
inline array<ll,2> Query(int x,int l,int r,ll k){
	if(l>r) return {0,0};
	if(l<=tr[x].l&&tr[x].r<=r) return {Calc(x,k),max(k,tr[x].mx)};
	int mid=tr[x].l+tr[x].r>>1;
	if(r<=mid) return Query(x<<1,l,r,k);
	else if(l>mid) return Query(x<<1|1,l,r,k);
	else{
		auto lv=Query(x<<1,l,r,k);
		auto rv=Query(x<<1|1,l,r,lv[1]);
		return {lv[0]+rv[0],rv[1]};
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);

	cin>>m>>q;
	for(int i=1;i<=q;i++){
		cin>>op[i];
		if(op[i]==1) ar[i].resize(2);
		else if(op[i]==2) ar[i].resize(1);
		else ar[i].resize(4);
		for(int &x:ar[i]) cin>>x;
	}

	for(int i=1;i<=q;i++){
		if(op[i]==1){
			n++;
			eu[n]=ar[i][0];
			ev[n]=ar[i][1];
			id[i]=n;
			st[id[i]]=i;
			ed[id[i]]=q;
		}else if(op[i]==2){
			id[i]=ar[i][0];
			ed[id[i]]=i-1;
		}
	}
	Init();
	for(int i=1;i<=n;i++) Insert(1,1,q,st[i],ed[i],eu[i],ev[i]);
	for(int i=1;i<=m;i++) lst[i]=now[i]=i;
	Conquer(1,1,q);
	
	Build(1,1,m);
	for(int i=1;i<=q;i++){
		if(op[i]==3){
			ll ans=0;
			int mnl=ar[i][0],mxl=ar[i][1],mnr=ar[i][2],mxr=ar[i][3];
			mxl=min(mxl,mxr),mnr=max(mnr,mnl);
			if(mnl>mxl||mnr>mxr){
				cout<<0<<endl;
				continue ;
			}
			ans=1ll*(mxl-mnl+1)*(mxr-mnr+1);
			int len=max(0,(min(mxl,mxr)-max(mnl,mnr)+1));
			ans-=1ll*len*(len-1)/2;
			ans-=Query(1,1,mxr,mnl-1)[0]-1ll*mxr*(mnl-1);
			ans+=Query(1,1,mnr-1,mnl-1)[0]-1ll*(mnr-1)*(mnl-1);
			ans+=Query(1,1,mxr,mxl)[0]-1ll*mxr*mxl;
			ans-=Query(1,1,mnr-1,mxl)[0]-1ll*(mnr-1)*mxl;
			cout<<ans<<endl;
		}else for(auto p:upd[i]) Modify(1,p[0],p[1]);
	}

	return 0;
}

C. 奇迹

何意味。

对每一位统计这一位为 \(0/1/2\)\(\sum A,\sum B,\sum C\),带入到 \(op\) 中看是否合法,时间复杂度 \(O(n\cdot 3^9\cdot 3^2)\)

因为是随机的所以正确性是对的。

场了,代码懒得复现了。

Day 3

A. 无题

处于某种原因,手玩了发现 \(k\) 的答案的端点是 \(k+1\) 的答案的端点的子集,因此直接贪心地找到添加之后能使答案最优的点,加入到当前的端点集合即可,时间复杂度 \(O(n\log n+q)\)

正确性完全不会证,能过就是好算法。

代码懒得复现了。

B. 复读机

这我哪会啊。

C. 解开尘封的序列

\(p=2\)

首先想到,可以把 \(\operatorname{xor}\) 拆成 \(\operatorname{or}-\operatorname{and}\),那么只有两种运算了,且算完 \(\operatorname{popcount}\) 之后本质不同的结果只有 \(O(d^2)\) 种。

考虑这 \(O(d^2)\) 对数的相对顺序,事实证明这样的顺序数是很少的。因为 \((A-C)x_0+(B+C)y_0\leq (A-C)x_1+(B+C)y_1\) 的限制事实上是对 \((A,B,C)\) 有一个 \(\dfrac{A-C}{B+C}\leq \dfrac{y_1-y_0}{x_0-x_1}\) 的一维半平面的限制。所以事实上被划分成的区间共有 \(O(d^2)\) 个,更具体地,\(\displaystyle 2\sum_{i=1}^d\varphi(i)\) 个。

因此,这允许我们对每种不同的顺序维护答案。对于某种特定的顺序,考虑对 \(0\sim 2^d-1\) 这一维扫描线,并分别维护 \(x,y\) 的值,即 \((A-C)\)\((B+C)\) 的系数,每个时刻都是 \(O(d^2)\) 次区间加,总的就是 \(O(d^2\cdot 2^dd^2)\) 次区间加,以及 \(O(q)\) 次区间查。使用分块平衡复杂度,总时间复杂度就是 \(O(4^d+2^dd^4+q\sqrt n)\)\(O(4^d)\) 的来源是预处理对于某个 \(x\)\((\operatorname{popc}(x\operatorname{and}y),\operatorname{popc}(x\operatorname{or}y))\) 为某一组特定的 \((p,q)\)\(y\) 的个数。

\(p=3\)

依然考虑对每种不同的顺序单独处理,由于 \(\operatorname{xor}\) 不能拆了,所以顺序数不能再套用之前的分析了。根据某些理论,有一个上界是 \(O(d^6)\),参见 CTT 讲题课件。

那么直接套用 \(p=2\) 的后半部分就可以做到 \(O(9^d+3^dd^9+q\sqrt n)\)

#include<bits/stdc++.h>

using namespace std;

#define endl '\n'
using ll=long long;
using uint=unsigned;
using ull=unsigned long long;
const int N=3e5+9;
const int D=1e1+9;
const int T=(1<<12)+9;

int a[N],n,d,p,q,lim;
int A[N],B[N],C[N],l1[N],r1[N],l2[N],r2[N];
uint w[N],z[T];

struct Block{
	int blk[N],L[N],R[N],S;
	uint sum[N],suf[N],v[N],bs[N],bv[N];
	inline void Init(){
		S=sqrt(n);
		for(int i=1;i<=n;i++) blk[i]=(i-1)/S+1;
		for(int i=1;i<=n;i++) R[blk[i]]=i;
		for(int i=n;i>=1;i--) L[blk[i]]=i;
		for(int i=1;i<=n;i++) sum[blk[i]]+=w[i];
		for(int i=1;i<=blk[n];i++){
			suf[R[i]]=w[R[i]];
			for(int j=R[i]-1;j>=L[i];j--) suf[j]=suf[j+1]+w[j];
		}
	}
	inline void Clear(){
		for(int i=1;i<=n;i++) v[i]=0;
		for(int i=1;i<=blk[n];i++) bs[i]=bv[i]=0;
	}
	inline void Modify(int pos,uint k){
		if(pos<1||pos>n) return ;
		v[pos]+=k,bv[blk[pos]]+=k,bs[blk[pos]]+=suf[pos]*k;
	}
	inline void Modify(int l,int r,uint k){Modify(l,k),Modify(r+1,-k);}
	inline uint Query(int pos){
		if(pos<1||pos>n) return 0;
		uint ans=0,cur=0;
		for(int i=1;i<blk[pos];i++){
			ans+=bs[i]+cur*sum[i];
			cur+=bv[i];
		}
		for(int i=L[blk[pos]];i<=pos;i++){
			cur+=v[i];
			ans+=cur*w[i];
		}
		return ans;
	}
	inline uint Query(int l,int r){return Query(r)-Query(l-1);}
};
namespace P2{
	uint sw[N],sz[N],ans[N];
	int cnt[T],c[T][D][D],tot;
	vector<int> qry[N];
	vector<array<int,2>> ord[T],opr[T];
	
	const ull base=4649;
	inline ull Hash(vector<array<int,2>> v){
		ull res=0;
		for(auto p:v) for(int x:p) res=res*base+x;
		return res;
	}
	#define popc __builtin_popcount
	
	Block bx,by;
	inline void Solve(){
		for(int i=1;i<=n;i++) cnt[a[i]]++;
		for(int i=0;i<lim;i++){
			for(int j=0;j<lim;j++) c[i][popc(i&j)][popc(~i&j)]+=cnt[j];
		}
		for(int i=1;i<=n;i++) sw[i]=sw[i-1]+w[i];
		for(int i=0;i<lim;i++) sz[i]=(i?sz[i-1]:0)+z[i]*popc(i);

		vector<array<int,2>> tmp;
		for(int i=0;i<=d;i++){
			for(int j=0;i+j<=d;j++) tmp.push_back({i,j});
		}
		map<ull,int> mp;
		for(int i=1;i<=q;i++){
			ll X=A[i]-C[i],Y=B[i]+C[i];
			sort(tmp.begin(),tmp.end(),[&](auto x,auto y){
				return X*x[0]+Y*x[1]<X*y[0]+Y*y[1];
			});
			ull h=Hash(tmp);
			if(!mp[h]) mp[h]=++tot,ord[tot]=tmp;
			qry[mp[h]].push_back(i);
		}

		bx.Init(),by.Init();
		for(int o=1;o<=tot;o++){
			for(int i:qry[o]){
				if(l1[i]) opr[l1[i]-1].push_back({i,-1});
				opr[r1[i]].push_back({i,1});
			}
			for(int i=0;i<lim;i++){
				int k=0;
				for(auto p:ord[o]){
					if(!c[i][p[0]][p[1]]) continue ;
					bx.Modify(k+1,k+c[i][p[0]][p[1]],z[i]*p[0]);
					by.Modify(k+1,k+c[i][p[0]][p[1]],z[i]*p[1]);
					k+=c[i][p[0]][p[1]];
				}
				for(auto p:opr[i]){
					int j=p[0],k=p[1];
					ans[j]+=k*(A[j]-C[j])*bx.Query(l2[j],r2[j]);
					ans[j]+=k*(B[j]+C[j])*by.Query(l2[j],r2[j]);
				}
				opr[i].clear();
			}
			bx.Clear(),by.Clear();
		}

		for(int i=1;i<=q;i++){
			ans[i]+=(B[i]+C[i])*(sw[r2[i]]-sw[l2[i]-1])*(sz[r1[i]]-(l1[i]?sz[l1[i]-1]:0));
			cout<<ans[i]<<endl;
		}
	}
	#undef popc
}
namespace P3{
	int popc[T],and3[T][T],or3[T][T],xor3[T][T];
	inline void Init(){
		for(int i=0;i<lim;i++) popc[i]=popc[i/3]+bool(i%3);
		for(int i=0;i<lim;i++){
			for(int j=0;j<lim;j++){
				and3[i][j]=and3[i/3][j/3]*3+min(i%3,j%3);
				or3[i][j]=or3[i/3][j/3]*3+max(i%3,j%3);
				xor3[i][j]=xor3[i/3][j/3]*3+(i+j)%3;
			}
		}
	}

	uint ans[N];
	int cnt[T],c[T][D][D][D],tot;
	vector<int> qry[N];
	vector<array<int,2>> opr[T];
	vector<array<int,3>> ord[N];

	const ull base=4649;
	inline ull Hash(vector<array<int,3>> v){
		ull res=0;
		for(auto p:v) for(int x:p) res=res*base+x;
		return res;
	}

	Block bx,by,bz;
	inline void Solve(){
		Init();
		for(int i=1;i<=n;i++) cnt[a[i]]++;
		for(int i=0;i<lim;i++){
			for(int j=0;j<lim;j++){
				c[i][popc[and3[i][j]]][popc[or3[i][j]]][popc[xor3[i][j]]]+=cnt[j];
			}
		}

		vector<array<int,3>> tmp;
		for(int i=0;i<lim;i++){
			for(int j=0;j<lim;j++){
				tmp.push_back({popc[and3[i][j]],popc[or3[i][j]],popc[xor3[i][j]]});
			}
		}
		sort(tmp.begin(),tmp.end());
		tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());

		map<ull,int> mp;
		for(int i=1;i<=q;i++){
			ll X=A[i],Y=B[i],Z=C[i];
			sort(tmp.begin(),tmp.end(),[&](auto x,auto y){
				ll fx=X*x[0]+Y*x[1]+Z*x[2];
				ll fy=X*y[0]+Y*y[1]+Z*y[2];
				if(fx!=fy) return fx<fy;
				else return x<y;
			});
			ull h=Hash(tmp);
			if(!mp[h]) mp[h]=++tot,ord[tot]=tmp;
			qry[mp[h]].push_back(i);
		}

		bx.Init(),by.Init(),bz.Init();
		for(int o=1;o<=tot;o++){
			for(int i:qry[o]){
				if(l1[i]) opr[l1[i]-1].push_back({i,-1});
				opr[r1[i]].push_back({i,1});
			}
			for(int i=0;i<lim;i++){
				int k=0;
				for(auto p:ord[o]){
					if(!c[i][p[0]][p[1]][p[2]]) continue ;
					bx.Modify(k+1,k+c[i][p[0]][p[1]][p[2]],z[i]*p[0]);
					by.Modify(k+1,k+c[i][p[0]][p[1]][p[2]],z[i]*p[1]);
					bz.Modify(k+1,k+c[i][p[0]][p[1]][p[2]],z[i]*p[2]);
					k+=c[i][p[0]][p[1]][p[2]];
				}
				for(auto p:opr[i]){
					int j=p[0],k=p[1];
					ans[j]+=k*A[j]*bx.Query(l2[j],r2[j]);
					ans[j]+=k*B[j]*by.Query(l2[j],r2[j]);
					ans[j]+=k*C[j]*bz.Query(l2[j],r2[j]);
				}
				opr[i].clear();
			}
			bx.Clear(),by.Clear(),bz.Clear();
		}

		for(int i=1;i<=q;i++) cout<<ans[i]<<endl;
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);

	cin>>n>>d>>p;
	lim=round(powl(p,d));
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>w[i];
	for(int i=0;i<lim;i++) cin>>z[i];
	cin>>q;
	for(int i=1;i<=q;i++) cin>>A[i]>>B[i]>>C[i]>>l1[i]>>r1[i]>>l2[i]>>r2[i];

	if(p==2) P2::Solve();
	else P3::Solve();

	return 0;
}
posted @ 2025-12-09 17:11  JoeyJiang  阅读(59)  评论(0)    收藏  举报