Description
比特镇的路网由 m 条双向道路连接的 n 个交叉路口组成。
最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。
比赛的路线要按照如下方法规划: 1、先选择三个两两互不相同的路口 s, c 和 f
,分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。 2、选择一条从 s 出发,经过 c 最终到达 f
的路径。考虑到安全因素,选择的路径经过同一个点至多一次。 在规划路径之前,镇长想请你帮忙计算,总共有多少种不同的选取 s, c 和 f
的方案,使得在第 2 步中至少能设计出一条满足要求的路径。
Input
第一行包含两个整数 n 和 m ,分别表示交叉路口和双向道路的数量。 接下来 m 行,每行两个整数 v_i, u_i
。表示存在一条双向道路连接交叉路口 v_i, u_i (1 <= v_i, u_i <= n, v_i != u_i)。
保证任意两个交叉路口之间,至多被一条双向道路直接连接。 n<=1e5, m<=2e5
Output
输出一行,包含一个整数,表示能满足要求的不同的选取s,c和f的方案数
Sample Input
4 3
1 2
2 3
3 4
Sample Output
8
题解
建议前往loj服用以获得更好体验…
似乎我的做法和网上的都不太一样啊…
感觉我的就是个弟弟…
首先先考虑树怎么做…可以用一个 表示以i点为根的子树中找出两个合法点的方案数
然后二次扫描就可以获得10分的好成绩了
拓展到图上
可以考虑缩一个点双出来,比如建一棵圆方树
同样预处理这个 ,在方点的时候就记录的是下面的圆点的 ,圆点就和树一样做
转移可以看 代码…大概很好懂
同样二次扫描
进环的时候要特殊处理一下…大致就是环上的点可以和这整棵子树上的所有点构成一个合法的二元组
估计也 能看懂的…
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline LL read()
{
LL f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
LL stack[20];
inline void write(LL x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
LL top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(LL x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const LL MAXN=500005;
const LL MAXM=500005;
struct node{LL x,y,next;}a[2*MAXM],b[2*MAXM];LL len,last[MAXN],lenb,lastb[2*MAXN];
void ins(LL x,LL y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
void ins_b(LL x,LL y){lenb++;b[lenb].x=x;b[lenb].y=y;b[lenb].next=lastb[x];lastb[x]=lenb;}
LL dfn[MAXN],low[MAXN],sta[MAXN],cnt,id,tp,root,n,m;
LL r[MAXN];
void link(){for(LL i=1;i<=r[0];i++)ins_b(r[i],cnt),ins_b(cnt,r[i]);}
void tarjan(LL x,LL fa)
{
low[x]=dfn[x]=++id;sta[++tp]=x;
for(LL k=last[x];k;k=a[k].next)
if(a[k].y!=fa)
{
LL y=a[k].y;
if(!dfn[y])
{
tarjan(y,x);low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])ins_b(y,x),ins_b(x,y),tp--;
else if(low[y]==dfn[x])
{
cnt++;r[r[0]=1]=x;
LL i;
do
{
i=sta[tp--];
r[++r[0]]=i;
}while(i!=y);
link();
}
}
else low[x]=min(low[x],dfn[y]);
}
}
bool vis[2*MAXN];
LL f[2*MAXN],g[2*MAXN],ans,tot[MAXN],siz[MAXN];//这个方点在的环有多少个点
void dp1(LL x,LL fa)
{
tot[x]=(x<=n);vis[x]=true;
for(LL k=lastb[x];k;k=b[k].next)
{
LL y=b[k].y;
if(y!=fa)
{
dp1(y,x);tot[x]+=tot[y];
if(y<=n)
{
if(x<=n)f[x]+=f[y]+tot[y],g[x]+=tot[y];
else f[x]+=f[y],siz[x]++,g[x]+=g[y];
}
else f[x]+=(siz[y])*(tot[y]-1)+tot[y]+f[y]-g[y],g[x]+=tot[y];
}
}
}
LL s1;
void dp2(LL x,LL fa,LL u1)//上面选两个点的方案数 有多少点在上面
{
if(x<=n)ans+=u1;LL u2=s1-tot[x];
for(LL k=lastb[x];k;k=b[k].next)
{
LL y=b[k].y;
if(y!=fa)
{
if(y>n)
{
ans+=(siz[y])*(tot[y]-1)+f[y]-g[y];
dp2(y,x,u1+f[x]-(siz[y]*(tot[y]-1)+tot[y]+f[y]-g[y])+u2);//这是个方点
}
else//这是个圆点
{
if(x<=n)
{
ans+=f[y];
dp2(y,x,u1+f[x]-(f[y]+tot[y])+u2);
}
else
{
LL ring=siz[x]+1;//这个环有这么多点
LL sum=f[x]-f[y]+u1;
sum+=(LL)(s1-tot[y]-ring+1)*(ring-2)+(ring-1)*(ring-2);//剩下有这么多
dp2(y,x,sum);
}
}
}
}
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
n=read();m=read();
for(LL i=1;i<=m;i++)
{
LL x=read(),y=read();
ins(x,y);ins(y,x);
}
cnt=n;
for(LL i=1;i<=n;i++)if(!dfn[i])tarjan(i,0);
// puts("");
// for(LL i=1;i<=lenb;i+=2)printf("%d %d\n",b[i].x,b[i].y);
for(LL i=1;i<=n;i++)if(!vis[i]){
dp1(i,0);s1=tot[i];
dp2(i,0,0);
}
pr2(ans);
return 0;
}
/*
4 4
1 2
2 3
3 4
4 2
14
*/