P4630 [APIO2018] Duathlon 铁人两项
Posted bztMinamoto
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4630 [APIO2018] Duathlon 铁人两项相关的知识,希望对你有一定的参考价值。
完全没想到圆方树orz……
我们先考虑建出这个图的圆方树,如果把方点的权值设为这个点双的大小,圆点的权值为(-1),那么起点(s)终点(f)的方案数就是这条路径上的权值总和,这样的话就可以做到(O(n^2))
然后考虑用dfs优化,即计算每个点被经过了几次,那么就可以做到(O(n))了
顺便注意起点和终点互换属于不同的方案,所以不要漏了
//minamoto
#include<bits/stdc++.h>
#define ll long long
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(G,u) for(register int i=G.head[u],v=G.e[i].v;i;i=G.e[i].nx,v=G.e[i].v)
using namespace std;
inline int max(const int &x,const int &y){return x>y?x:y;}
inline int min(const int &x,const int &y){return x<y?x:y;}
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
int res,f=1;char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=5e5+5;
struct eg{int v,nx;};
struct Gr{
eg e[N];int head[N],tot;
inline void add(int u,int v){e[++tot]={v,head[u]},head[u]=tot;}
}G,T;
int n,m,tim,tot,x,u,v,top,dfn[N],low[N],st[N],val[N],sz[N],sum;ll ans;
void tarjan(int u){
dfn[u]=low[u]=++tim,st[++top]=u;
sz[u]=1,val[u]=-1;
go(G,u)if(dfn[v])low[u]=min(low[u],dfn[v]);
else{
tarjan(v),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
T.add(u,++tot),val[tot]=1;
do{
x=st[top--],T.add(tot,x);
sz[tot]+=sz[x],++val[tot];
}while(x!=v);
sz[u]+=sz[tot];
}
}
}
void dfs(int u){
if(u<=n)ans+=1ll*(sum-1)*val[u];
ans+=1ll*(sum-sz[u])*sz[u]*val[u];
go(T,u)ans+=1ll*(sum-sz[v])*sz[v]*val[u],dfs(v);
}
int main(){
// freopen("testdata.in","r",stdin);
tot=n=read(),m=read();
while(m--)u=read(),v=read(),G.add(u,v),G.add(v,u);
fp(i,1,n)if(!dfn[i])tarjan(i),sum=sz[i],dfs(i);
printf("%lld
",ans);return 0;
}
以上是关于P4630 [APIO2018] Duathlon 铁人两项的主要内容,如果未能解决你的问题,请参考以下文章
[圆方树] Luogu P4630 Duathlon 铁人两项