abc248 (F-Ex)

https://atcoder.jp/contests/abc248

G

\[\sum_{i=1}^n\sum_{j=i+1}^n C(i,j) \]

关于 gcd 有关的要不就莫反要不就欧拉反演。

\(\gcd(S)=\sum_{d|\gcd(S)}\varphi(d)=\sum_{d|x,x\in S}\varphi(d)\)

那么我们可以枚举 \(d\),抽离出 d 的倍数的图,那么这张图上的任意一条路径 d 都是可以贡献到的,总贡献系数是什么?显然就是所有路径经过的点的数量总和。这个东西可以树形 dp,钦定一个点求出。然后就做完了。

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int mod=998244353,N=(int)(1e5+5);
bool vis[N];
int n,a[N],phi[N],pri[N],NW,cntt;

void init() {
	phi[1]=1;
	for(int i=2;i<=N-5;i++) {
		if(!vis[i]) pri[++cntt]=i,phi[i]=i-1;
		for(int j=1;j<=cntt&&pri[j]*i<=N-5;j++) {
			vis[i*pri[j]]=1;
			if(i%pri[j]==0) {
				phi[i*pri[j]]=phi[i]*pri[j]; break ;
			}
			phi[i*pri[j]]=phi[i]*phi[pri[j]];
		}
	}
}
vector<int>vec[N];
bool fl[N];
int tot,b[N];
void sol(int x,int id) {
	for(int i=1;i*i<=x;i++) {
		if(x%i==0) {
			if(!fl[i]) fl[i]=1,b[++tot]=i;
			vec[i].push_back(id);
			if(i*i==x) continue ;
			if(!fl[x/i]) fl[x/i]=1,b[++tot]=x/i;
			vec[x/i].push_back(id); 
		}
	}
}

struct edge {
	int nex,to;
}e[N<<1];
int cnt,hea[N],sz[N],XGF,rtsz;
void add_edge(int x,int y) {
	e[++cnt].nex=hea[x]; e[cnt].to=y; hea[x]=cnt;
}

void dfs(int x,int ff) {
	fl[x]=1; sz[x]=1;
	for(int i=hea[x];i;i=e[i].nex) {
		int y=e[i].to; if(y==ff||a[y]%NW) continue ;
		dfs(y,x); sz[x]+=sz[y];
	}
}

void dfs2(int x,int ff) {
	fl[x]=0; int qwq=rtsz-sz[x]+1;
	XGF=(XGF+qwq-1)%mod;
	for(int i=hea[x];i;i=e[i].nex) {
		int y=e[i].to; if(y==ff||!fl[y]) continue ;
		XGF=(XGF+qwq*sz[y]%mod)%mod;
		qwq=qwq+sz[y];
	}
	for(int i=hea[x];i;i=e[i].nex) {
		int y=e[i].to; if(y==ff||!fl[y]) continue ;
		dfs2(y,x);
	}
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	init();
	cin>>n; for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++) {
		int x,y; cin>>x>>y; add_edge(x,y); add_edge(y,x);
	}
	for(int i=1;i<=n;i++) sol(a[i],i);
	memset(fl,0,sizeof(fl));
	int ans=0;
	for(int i=1;i<=tot;i++) {
		NW=b[i]; XGF=0;
		for(int j=1;j<=n;j++) fl[i]=0;
		for(int x:vec[NW]) {
			if(!fl[x]) dfs(x,0);
		}
		for(int x:vec[NW]) {
			if(fl[x]) rtsz=sz[x],dfs2(x,0);
		}
		ans=(ans+XGF*phi[NW]%mod)%mod; 
	}
	ans=(ans%mod+mod)%mod;
	cout<<ans;
	return 0;
}

Ex

没啥好说的,扫描线扫 R,DS 下标的 L 维护 \([L,R]\) 区间的 \(\max+\min\),发现预处理单调栈然后遇到 i,那么它当最大值的区间的左端点范围应该是 \([Lmx_i+1,i]\),需要注意的是当扫到了右边第一个比 i 大的数时,就需要消去 i 的贡献。发现需要支持区间加,区间小于等于某个数的个数,分块即可。

#include <bits/stdc++.h>
#define pb push_back
#define il inline
using namespace std;
// Li 表示左边第一个 比它大的
// 那么扫 R,每次覆盖都是 [Li+1,i]
// Ri到了那么就要消去贡献了 
const int N=(int)(2e+5),M=500;
vector<int>vec[N],vec2[N];
int st[N],top,B[M][M],sz[M];
int n,m,K,bl,id[N],L[M],R[M],a[N],Lmx[N],Rmx[N],Lmi[N],Rmi[N],sum[N];
int tag[M];
il void build(int x) {
	sz[x]=0;
	for(int i=L[x];i<=R[x];i++) B[x][++sz[x]]=sum[i];
}

il void rebuild(int x) {
	sz[x]=0;
	for(int i=L[x];i<=R[x];i++) B[x][++sz[x]]=sum[i];
	sort(B[x]+1,B[x]+1+sz[x]);
}

il void upt(int x,int y,int v) {
	if(id[x]==id[y]) {
		for(int i=x;i<=y;i++) sum[i]+=v;
		rebuild(id[x]);
	} else {
		for(int i=x;i<=R[id[x]];i++) sum[i]+=v;
		for(int i=L[id[y]];i<=y;i++) sum[i]+=v;
		rebuild(id[x]); rebuild(id[y]);
		for(int i=id[x]+1;i<id[y];i++) tag[i]+=v; 
	}
}

il int qry(int x,int y,int Lim) {
	int res=0;
	if(id[x]==id[y]) {
		for(int i=x;i<=y;i++) if(sum[i]+tag[id[x]]<=Lim) ++res;
	} else {
		for(int i=x;i<=R[id[x]];i++) if(sum[i]+tag[id[x]]<=Lim) ++res;
		for(int i=L[id[y]];i<=y;i++) if(sum[i]+tag[id[y]]<=Lim) ++res;
		for(int i=id[x]+1;i<id[y];i++) {
			int p=upper_bound(B[i]+1,B[i]+1+sz[i],Lim-tag[i])-B[i]-1;
			res+=p;
		}
	}
	return res;
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	cin>>n>>K; m=3*n;
	for(int i=1;i<=n;i++) cin>>a[i];
	top=0;
	for(int i=1;i<=n;i++) {
		while(top&&a[st[top]]<a[i]) Rmx[st[top]]=i,--top;
		st[++top]=i;
	}
	top=0;
	for(int i=n;i>=1;i--) {
		while(top&&a[st[top]]<a[i]) Lmx[st[top]]=i,--top;
		st[++top]=i;
	}
	top=0;
	for(int i=1;i<=n;i++) {
		while(top&&a[st[top]]>a[i]) Rmi[st[top]]=i,--top;
		st[++top]=i;
	}
	top=0;
	for(int i=n;i>=1;i--) {
		while(top&&a[st[top]]>a[i]) Lmi[st[top]]=i,--top;
		st[++top]=i;
	}
	for(int i=1;i<=n;i++) {
		if(Rmx[i]) vec[Rmx[i]].pb(i);
		if(Rmi[i]) vec2[Rmi[i]].pb(i);
	}
	long long ans=0; bl=sqrt(n);
	for(int i=1;i<=n;i++) sum[i]=i;
	for(int i=1;i<=n;i++) id[i]=(i-1)/bl+1;
	for(int i=1;i<=id[n];i++) L[i]=(i-1)*bl+1,R[i]=i*bl; R[id[n]]=n;
	for(int i=1;i<=id[n];i++) build(i); 
	for(int i=1;i<=n;i++) {
		upt(Lmx[i]+1,i,a[i]); upt(Lmi[i]+1,i,-a[i]);
		for(int x:vec[i]) upt(Lmx[x]+1,x,-a[x]);
		for(int x:vec2[i]) upt(Lmi[x]+1,x,a[x]);
		ans+=qry(1,i,i+K);
	}
	cout<<ans;
	return 0;
}

posted @ 2022-04-19 16:59  FxorG  阅读(81)  评论(0编辑  收藏  举报