abc248 (F-Ex)

https://atcoder.jp/contests/abc248

G

i=1nj=i+1nC(i,j)

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

gcd(S)=d|gcd(S)φ(d)=d|x,xSφ(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,那么它当最大值的区间的左端点范围应该是 [Lmxi+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; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/16166153.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(88)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示