• abc248 (FEx)


    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;
    }
    
    
  • 相关阅读:
    四则运算在线答题dos
    动手动脑11.11
    Java编程中关于异常处理的10个要点
    java第五周课后作业1
    java静态初始化块的执行顺序
    java工程项目作业1
    十一Java作业三
    Linux运维笔记(一)网络基础知识
    分布式笔记(二)一致性协议
    分布式笔记(一)分布式构架概述
  • 原文地址:https://www.cnblogs.com/xugangfan/p/16166153.html
Copyright © 2020-2023  润新知