【做题记录】HZOJ 多校-数论/多校-字符串/多校-图论I

还剩三个专题,准备一块做,随了个做题顺序(当然太难了或者没学我就跳了)

做题顺序

01. 数论 M. [arc112_f]Die Siedler

考虑将一个状态 \(A=\{c_1,c_2,\dots,c_n\}\) 映射到一个数 \(X=\sum_{i=1}^{n}2^{i-1}(i-1)!c_i\),即将所有卡牌都倒推回 \(c_1\)。不难发现如果我们只进行 \(i\in[1,n-1]\) 的换牌则 \(X\) 不会改变,否则若进行 \(i=n\) 的换牌则 \(X\gets X-2^nn!+1\)。且对于最终状态,必有 \(X<2^nn!\)

设初始的 \(X=P\),第 \(i\) 个卡包的 \(X=a_i\),最终的 \(X=Q\)。若第 \(i\) 个卡包使用了 \(x_i\) 次,进行了 \(y\)\(i=n\) 的换牌,则有:

\[Q=P+\sum_{i=1}^{m}x_ia_i-y(2^nn!-1) \]

\(d=\gcd(a_1,a_2,\dots,a_n,2^nn!-1)\),则由裴蜀定理有 \(Q\equiv P\pmod{d}\)。设 \(r=P\bmod d\),于是 \(Q=kd+r,k\in\mathbb{N}\)。设 \(f(X)\) 表示最终状态最小有多少张牌,于是我们只需求 \(\min\{f(kd+r)\}\)。此时不难有两种暴力:

  • 直接暴力枚举 \(k\),计算 \(f\) 的值。\(O(n\frac{2^nn!-1}{d})\)

  • 同余最短路。初始有 \(\forall i\in[0,n-1],u=2^ii!\bmod d,dis_u=1\),每次枚举选择哪种牌。时间复杂度 \(O(nd)\)

考虑根号分治,时间复杂度为 \(O(nD)\),其中 \(D\) 为最大的不超过 \(\sqrt{2^nn!-1}\) 的约数,为 \(1214827\)

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e6+5,inf=1e18;
int n,m,a[19],b[55][19],c[19],dis[maxn];
bool vis[maxn]; 
queue<int> q;
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	c[0]=1;
	int r=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		c[i]=c[i-1]*2*i;
		r+=a[i]*c[i-1];
	}
	int d=c[n]-1;
	for(int i=1;i<=m;i++){
		int t=0;
		for(int j=1;j<=n;j++){
			cin>>b[i][j];
			t+=b[i][j]*c[j-1];
		}
		d=__gcd(d,t);
	}
	r%=d;
//	cout<<d<<'\n';
	if(d<=1214827){
		for(int i=0;i<n;i++){
			dis[c[i]%d]=vis[c[i]%d]=1;
			q.push(c[i]%d);
		}
		while(q.size()){
			int u=q.front();
			q.pop();
			for(int i=0;i<n;i++){
				int v=(u+c[i])%d;
				if(!vis[v]){
					vis[v]=1;
					dis[v]=dis[u]+1;
					q.push(v);
				}
			}
		}
		cout<<dis[r];
	}else{
		int ans=inf;
		for(int i=r?r:d;i<c[n];i+=d){
			int res=0,tmp=i;
			for(int j=1;j<=n;j++){
				res+=tmp%(2*j);
				tmp/=2*j;
			}
			ans=min(ans,res);
		}
		cout<<ans;
	}
	return 0;
}
}
signed main(){return asbt::main();}

02. 数论 B. [POI2011] SEJ-Strongbox

2025noip模拟赛73 B

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=2.5e5+5;
int n,m,a[maxn];
vector<int> prm;
set<int> vis;
il void div(int x){
	for(int i=2;i<=x/i;i++){
		if(x%i==0){
			prm.pb(i);
			while(x%i==0){
				x/=i;
			}
		}
	}
	if(x>1){
		prm.pb(x);
	}
}
il void dfs(int x){
	if(vis.count(x)){
		return ;
	}
	vis.insert(x);
	for(int p:prm){
		if(x%p==0){
			dfs(x/p);
		}
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int d=__gcd(a[n],m);
	div(d);
	for(int i=1;i<n;i++){
		dfs(__gcd(a[i],d));
	}
	int i=1;
	for(;i<=d/i;i++){
		if(d%i==0&&!vis.count(i)){
			cout<<m/i;
			return 0;
		}
	}
	for(i--;i;i--){
		if(d%i==0&&!vis.count(d/i)){
			cout<<m/(d/i);
			return 0;
		}
	}
	return 0;
}
}
signed main(){return asbt::main();}

03. 数论 J. [arc084_b]Small Multiple

考虑一个数的各个数位和是怎么表示的。假设已经知道了 \(x\) 的各个数位和,不难想到两种转移:

\[x\xrightarrow{1}x+1\\ x\xrightarrow{0}10\cdot x \]

于是跑同余最短路即可。由于边权只有 \(0\)\(1\),可以使用 0-1bfs。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define pf push_front
#define pob pop_back
#define pof pop_front
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,dis[maxn];
bool vis[maxn];
deque<int> q;
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	memset(dis,0x3f,sizeof(dis));
	dis[1]=1,q.pb(1);
	while(q.size()){
		int u=q.front();
		q.pof();
		if(vis[u]){
			continue;
		}
		vis[u]=1;
		if(u==0){
			cout<<dis[u];
			break;
		}
		if(!vis[u+1]&&dis[u]+1<dis[u+1]){
			dis[u+1]=dis[u]+1,q.pb(u+1);
		}
		if(!vis[u*10%n]&&dis[u]<dis[u*10%n]){
			dis[u*10%n]=dis[u],q.pf(u*10%n);
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

04. 图论 I. Turtle and Multiplication

\(x\cdot y\ne x'\cdot y'\) 的必要条件是 \((x,y)\ne(x',y')\),其中 \((x,y),(x',y')\) 都是无序对。当 \(x,y,x',y'\) 都是质数时,这个条件就是充要的了。于是考虑只取质数。

假设我们要取 \(m\) 个点,相当于要求我们在一张无向完全图(含有自环)上跑出一条欧拉路径。如果 \(m\) 是奇数,则每个点的度数都是偶数,有欧拉回路;否则,每个点的度数都是奇数,我们需要删去一些边使这张图成为半欧拉图,显然删去 \(2\to3,4\to5\dots,m-2\to m-1\) 是最优的。于是我们先二分出 \(m\),再跑一遍欧拉路径即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=2e3+5,maxm=2e5+5;
int T,n,hd[maxn],enm,prm[maxm],prn;
bool ban[maxn*maxn],npr[maxm];
vector<int> ans;
struct{
	int v,nxt;
}e[maxn*maxn];
il void addedge(int u,int v){
	e[++enm]={v,hd[u]};
	hd[u]=enm;
}
il bool check(int x){
	if(x&1){
		return x*(x+1)/2+1>=n;
	}else{
		return x*(x+1)/2-x/2+2>=n;
	}
}
il void euler(int n=2e5){
	for(int i=2;i<=n;i++){
		if(!npr[i]){
			prm[++prn]=i;
		}
		for(int j=1;j<=prn&&i*1ll*prm[j]<=n;j++){
			npr[i*prm[j]]=1;
			if(i%prm[j]==0){
				break;
			}
		}
	}
}
il void dfs(int u){
	for(int &i=hd[u];i;i=e[i].nxt){
		if(ban[i]){
			continue;
		}
		int v=e[i].v;
		ban[i]=1;
		if(v!=u){
			ban[i^1]=1;
		}
		dfs(v);
	}
	ans.pb(prm[u]);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	euler();
	cin>>T;
	while(T--){
		cin>>n;
		int l=1,r=2e3;
		while(l<r){
			int mid=(l+r)>>1;
			if(check(mid)){
				r=mid;
			}else{
				l=mid+1;
			}
		}
		enm=1;
		for(int i=1;i<=l;i++){
			hd[i]=0;
		}
		for(int i=1;i<=l;i++){
			for(int j=i+1;j<=l;j++){
				if(l%2==0&&i%2==0&&j==i+1){
					continue;
				}
				addedge(i,j),addedge(j,i);
			}
		}
		for(int i=1;i<=l;i++){
			addedge(i,i);
		}
		for(int i=1;i<=enm;i++){
			ban[i]=0;
		}
		ans.clear();
		dfs(1);
//		cout<<l<<'\n';
		for(int i=0;i<n;i++){
			cout<<ans[i]<<' ';
		}
		cout<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

05. 数论 A. ConstructOR

首先考虑判无解。可以发现,如果 \(lowbit(a\operatorname{or}b)<lowbit(d)\),则必然无解。实际上它是充要的,因为如果不满足这个条件则一定可以构造。

我们考虑比原题要求更强一点的构造,题目要求 \(d|(a\operatorname{or}x),d|(b\operatorname{or}x)\),我们考虑直接构造 \(d|x\) 满足 \((a\operatorname{or}b)\subseteq x\)。那么实际上构造是非常简单的,从低往高扫每一位,如果 \(a\operatorname{or}b\) 这一位是 \(1\) 且当前答案 \(x\) 这一位不是 \(1\),就给 \(x\) 加上 \(d\) 的某个倍数,满足它的 \(lowbit\) 正好对到这一位。显然必然满足 \(x<2^{60}\)

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lowbit(x) ((x)&-(x))
using namespace std;
namespace asbt{
int T,a,b,d;
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>a>>b>>d;
		a|=b;
		int lab=__lg(lowbit(a)),ld=__lg(lowbit(d));
		if(ld>lab){
			cout<<-1<<'\n';
			continue;
		}
		int x=0;
		for(int i=0;i<=30;i++){
			if((a>>i&1)&&!(x>>i&1)){
				x+=(1ll<<(i-ld))*d;
			}
		}
		cout<<x<<'\n';
	}
	return 0;
}
}
signed main(){return asbt::main();}

06. 字符串 G. Legen...

建 AC 自动机,广义矩阵快速幂即可。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=205;
int n,m,tot,a[maxn],b[maxn],tr[maxn][26],fail[maxn];
vector<int> e[maxn];
queue<int> q;
struct juz{
	int a[maxn][maxn];
	juz(){
		memset(a,-0x3f,sizeof(a));
	}
	il int*operator[](int x){
		return a[x];
	}
	il juz operator*(juz b)const{
		juz c;
		for(int i=0;i<=tot;i++){
			for(int j=0;j<=tot;j++){
				for(int k=0;k<=tot;k++){
					c[i][j]=max(c[i][j],a[i][k]+b[k][j]);
				}
			}
		}
		return c;
	}
}bas;
il juz qpow(juz x,int y){
	juz res;
	for(int i=0;i<=tot;i++){
		res[i][i]=0;
	}
	while(y){
		if(y&1){
			res=res*x;
		}
		x=x*x,y>>=1;
	}
	return res;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		string s;
		cin>>s;
		int p=0;
		for(char c:s){
			int d=c-'a';
			if(!tr[p][d]){
				tr[p][d]=++tot;
			}
			p=tr[p][d];
		}
		b[p]+=a[i];
	}
	for(int i=0;i<=25;i++){
		if(tr[0][i]){
			q.push(tr[0][i]);
		}
	}
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=0;i<=25;i++){
			if(tr[u][i]){
				fail[tr[u][i]]=tr[fail[u]][i];
				q.push(tr[u][i]);
			}else{
				tr[u][i]=tr[fail[u]][i];
			}
		}
	}
	for(int i=1;i<=tot;i++){
		if(fail[i]){
			e[fail[i]].pb(i);
		}else{
			q.push(i);
		}
	}
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int v:e[u]){
			b[v]+=b[u],q.push(v);
		}
	}
	for(int i=0;i<=tot;i++){
		for(int j=0;j<=25;j++){
			bas[i][tr[i][j]]=b[tr[i][j]];
		}
	}
	bas=qpow(bas,m);
	int ans=0;
	for(int i=0;i<=tot;i++){
		ans=max(ans,bas[0][i]);
	}
	cout<<ans;
	return 0;
}
}
signed main(){return asbt::main();}

07. 图论 A. Ehab's Last Corollary

考虑先找环。如果找到了 \(\le k\) 的环,就直接输出。否则,要么没环,就是一棵树,跑一遍最大独立集再输出方案即可;要么必然有 \(>k\) 的环,在环上隔一个输出一个即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=4e5+5;
int n,m,kk,dp[maxn][2],cnt;
int vis[maxn],hd[maxn],enm=1;
int dfn[maxn],tot;
bool ban[maxn];
struct{
	int v,nxt;
}e[maxn];
struct{
	int u,v;
}E[maxn];
il void addedge(int u,int v){
	e[++enm]={v,hd[u]},hd[u]=enm;
}
il void dfs1(int u,int fa){
	dp[u][1]=1;
	for(int i=hd[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa){
			continue;
		}
		dfs1(v,u);
		dp[u][0]+=max(dp[v][0],dp[v][1]);
		dp[u][1]+=dp[v][0];
	}
}
il void dfs2(int u,int fa,int d){
	if(d){
		cout<<u<<' ';
		if(++cnt==(kk+1)>>1){
			exit(0);
		}
	}
	for(int i=hd[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa){
			continue;
		}
		if(d){
			dfs2(v,u,0);
		}else{
			dfs2(v,u,dp[v][1]>dp[v][0]);
		}
	}
}
il int dfs3(int u){
	dfn[u]=++cnt;
	for(int &i=hd[u];i;i=e[i].nxt){
		if(ban[i]){
			continue;
		}
		ban[i]=ban[i^1]=1;
		int v=e[i].v;
		if(!dfn[v]){
			int r=dfs3(v);
			if(r){
				cout<<u<<' ';
				if(r==u){
					exit(0);
				}
				return r;
			}
		}else{
			if(dfn[u]-dfn[v]+1<=kk){
				cout<<2<<'\n'<<dfn[u]-dfn[v]+1<<'\n'<<u<<' ';
				return v;
			}
		}
	}
	dfn[u]=0,cnt--;
	return 0;
}
il int dfs4(int u){
//	cout<<u<<'\n';
	dfn[u]=++cnt;
	for(int &i=hd[u];i;i=e[i].nxt){
		if(ban[i]){
			continue;
		}
		ban[i]=ban[i^1]=1;
		int v=e[i].v;
		if(!dfn[v]){
			int r=dfs4(v);
			if(r){
				if(r==1){
					cout<<u<<' ';
					if(++tot==(kk+1)>>1){
						exit(0);
					}
				}
				return -r;
			}
		}else{
			cout<<1<<'\n'<<u<<' ';
			if(++tot==(kk+1)>>1){
				exit(0);
			}
			return -1;
		}
	}
	dfn[u]=0,cnt--;
	return 0;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m>>kk;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		addedge(u,v),addedge(v,u);
		E[i]={u,v};
	}
	if(m==n-1){
		cout<<1<<'\n';
		dfs1(1,0);
//		for(int i=1;i<=n;i++){
//			cout<<dp[i][0]<<' '<<dp[i][1]<<'\n';
//		}
		dfs2(1,0,dp[1][1]>dp[1][0]);
		return 0;
	}
	dfs3(1);
//	puts("666");
	enm=1;
	for(int i=1;i<=m;i++){
		addedge(E[i].u,E[i].v);
		addedge(E[i].v,E[i].u);
	}
	for(int i=1;i<=enm;i++){
		ban[i]=0;
	}
//	for(int i=1;i<=n;i++){
//		cout<<dfn[i]<<' ';
//	}
	dfs4(1);
	return 0;
}
}
int main(){return asbt::main();}

09. 字符串 I. [NOI2011] 阿狸的打字机

首先建出 AC 自动机。考虑这个询问和 AC 自动机的关系,实际上就是从 \(y\) 的根链上的每个点不停跳 \(fail\),如果跳到了 \(x\) 的末尾就将答案加一。考虑到从同一个点跳 \(fail\) 不可能两次跳到同一个点,故可以转化为将 \(y\) 根链上的每个点的权值设为 \(1\),其它设为 \(0\),查询 \(fail\) 树上 \(x\) 末尾的子树的权值和。于是我们离线下来,给 \(fail\) 树跑个 \(dfn\),树状数组维护一下即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,ans[maxn],tot,tr[maxn][26],tra[maxn][26];
int fail[maxn],fa[maxn],end[maxn],dfn[maxn],cnt,sz[maxn];
string s;
vector<int> e[maxn],id[maxn];
vector<pii> qq[maxn];
queue<int> q;
il void dfs1(int u){
//	cout<<u<<'\n';
	dfn[u]=++cnt,sz[u]=1;
	for(int v:e[u]){
		dfs1(v);
		sz[u]+=sz[v];
	}
}
il void build(){
	int p=0;
	for(char i:s){
		if(i=='B'){
			p=fa[p];
		}else if(i=='P'){
			end[++n]=p,id[p].pb(n);
		}else{
			int d=i-'a';
			if(!tr[p][d]){
				tr[p][d]=tra[p][d]=++tot;
				fa[tot]=p;
			}
			p=tr[p][d];
		}
	}
	for(int i=0;i<=25;i++){
		if(tra[0][i]){
			q.push(tra[0][i]);
		}
	}
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=0;i<=25;i++){
			if(tra[u][i]){
				fail[tra[u][i]]=tra[fail[u]][i];
				q.push(tra[u][i]);
			}else{
				tra[u][i]=tra[fail[u]][i];
			}
		}
	}
	for(int i=1;i<=tot;i++){
		e[fail[i]].pb(i);
	}
	dfs1(0);
}
struct{
	#define lowbit(x) ((x)&-(x))
	int tr[maxn];
	il void add(int p,int x){
		for(;p<=cnt;p+=lowbit(p)){
			tr[p]+=x;
		}
	}
	il int query(int p){
		int res=0;
		for(;p;p-=lowbit(p)){
			res+=tr[p];
		}
		return res;
	}
	#undef lowbit
}F;
il void dfs2(int u){
//	cout<<u<<'\n';
	F.add(dfn[u],1);
	for(int y:id[u]){
		for(pii i:qq[y]){
			int x=i.fir;
			ans[i.sec]=F.query(dfn[end[x]]+sz[end[x]]-1)-F.query(dfn[end[x]]-1);
		}
	}
	for(int i=0;i<=25;i++){
		if(tr[u][i]){
			dfs2(tr[u][i]);
		}
	}
	F.add(dfn[u],-1);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>s>>m;
	for(int i=1,x,y;i<=m;i++){
		cin>>x>>y;
		qq[y].pb(mp(x,i));
	}
	build();
//	puts("666");
	dfs2(0);
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

10. 图论 J. Tree and Queries

直接树上莫队就完了。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5,B=316;
int n,m,a[maxn],dfn[maxn],sz[maxn],cnt,stk[maxn];
int bnm,bel[maxn],tong[maxn],cun[maxn],ans[maxn];
vector<int> e[maxn];
struct node{
	int l,r,k,id;
	il bool operator<(const node &x)const{
		return bel[l]!=bel[x.l]?l<x.l:bel[l]&1?r<x.r:r>x.r;
	}
}q[maxn];
il void dfs(int u,int fa){
	dfn[u]=++cnt,sz[u]=1,stk[cnt]=a[u];
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs(v,u);
		sz[u]+=sz[v];
	}
}
il void add(int x){
	cun[++tong[x]]++;
}
il void del(int x){
	cun[tong[x]--]--;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	dfs(1,0);
	for(int i=1,u,k;i<=m;i++){
		cin>>u>>k;
		q[i]={dfn[u],dfn[u]+sz[u]-1,k,i};
	}
	bnm=(n+B-1)/B;
	for(int i=1,st=0,ed=0;i<=bnm;i++){
		st=ed+1,ed=min(ed+B,n);
		for(int j=st;j<=ed;j++){
			bel[j]=i;
		}
	}
	sort(q+1,q+m+1);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		while(r<q[i].r){
			add(stk[++r]);
		}
		while(l>q[i].l){
			add(stk[--l]);
		}
		while(r>q[i].r){
			del(stk[r--]);
		}
		while(l<q[i].l){
			del(stk[l++]);
		}
		ans[q[i].id]=cun[q[i].k];
	}
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

11. 数论 S. LCM Sum (hard version)

考虑算出 \(\operatorname{lcm}(i,j,k)<i+j+k\) 的三元组数量,再用 \({r-l+1\choose 3}\) 减掉即可。

\(\operatorname{lcm}(i,j,k)=x,i=\frac{x}{a},j=\frac{x}{b},k=\frac{x}{c}\),于是上面那个式子即为 \(\frac{1}{a}+\frac{1}{b}+\frac{1}{c}>1\),并且 \(a>b>c\)。首先考虑 \(c>1\) 的情况。

如果 \(c\ge3\),那么 \(\frac{1}{a}+\frac{1}{b}+\frac{1}{c}<\frac{1}{3}+\frac{1}{3}+\frac{1}{3}=1\),不成立。于是 \(c=2\)

此时如果 \(b\ge4\),那么 \(\frac{1}{a}+\frac{1}{b}+\frac{1}{c}<\frac{1}{4}+\frac{1}{4}+\frac{1}{2}=1\),不成立。于是 \(b=3\)

类似地可以得到,\(a=4\text{ 或 }5\)。于是有 \(i:j:k=3:4:6\text{ 或 }6:10:15\),方案数可以非常容易地直接算出来。

然后是 \(c=1\) 的情况。此时相当于要求 \(i,j|k\)。考虑对于 \(\forall i\in[l,r]\),假设其在 \([l,r]\) 中的因子有 \(cnt_i\) 个,则贡献为 \({cnt_i\choose2}\)。考虑离线,对 \(l\) 倒着扫描线,枚举所有倍数,在树状数组上修改贡献即可。时间复杂度线性对数方。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,ans[maxn],d[maxn]; 
vector<pii> q[maxn];
struct{
	#define lowbit(x) ((x)&-(x))
	int tr[maxn];
	il void add(int p,int x){
		for(;p<=2e5;p+=lowbit(p)){
			tr[p]+=x;
		}
	}
	il int query(int p){
		int res=0;
		for(;p;p-=lowbit(p)){
			res+=tr[p];
		}
		return res;
	}
	#undef lowbit
}F;
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1,l,r;i<=n;i++){
		cin>>l>>r;
		int k=r-l+1;
		ans[i]=k*(k-1)*(k-2)/6-max(0ll,r/6-(l+2)/3+1)-max(0ll,r/15-(l+5)/6+1);
		q[l].pb(mp(r,i));
	}
	for(int l=2e5;l;l--){
		for(int i=l*2;i<=2e5;i+=l){
			F.add(i,(d[i]+1)*d[i]/2-d[i]*(d[i]-1)/2);
			d[i]++;
		}
		for(pii i:q[l]){
			int r=i.fir;
			ans[i.sec]-=F.query(r)-F.query(l-1);
		}
	}
	for(int i=1;i<=n;i++){
		cout<<ans[i]<<'\n';
	}
	return 0;
}
}
signed main(){return asbt::main();}

12. 图论 K. XOR Tree

考虑将 \(a_u\) 变成一个非常大的 \(2\) 整次幂(如 \(2^{u+11^{4514}}\)),我们就可以保证所有经过 \(u\) 的路径合法。考虑贪心,如果 \(u\) 子树中有一条经过 \(u\) 的路径不合法,则必须将 \(a_u\) 值修改,此后便可以不考虑这棵子树。启发式合并,时间复杂度线性对数方。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,a[maxn],d[maxn],ans;
vector<int> e[maxn];
set<int> s[maxn];
il void dfs1(int u,int fa){
	d[u]=d[fa]^a[u];
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs1(v,u);
	}
}
il void dfs2(int u,int fa){
	s[u].insert(d[u]);
	bool flag=0;
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs2(v,u);
		if(flag){
			continue;
		}
		if(s[u].size()<s[v].size()){
			swap(s[u],s[v]);
		}
		for(int x:s[v]){
			if(s[u].count(x^a[u])){
				ans++,flag=1,s[u].clear();
				goto togo;
			}
		}
		for(int x:s[v]){
			s[u].insert(x);
		}
		togo:;
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	dfs1(1,0),dfs2(1,0);
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}
/*
10
7 4 6 7 6 6 7 5 7 5
8 7
4 5
9 6
2 5
4 8
9 10
4 3
9 4
1 8
*/
posted @ 2025-11-12 21:57  zhangxy__hp  阅读(19)  评论(0)    收藏  举报