NC14394 手铐
题目大意
给你 N 个顶点 M 条边,问你可以组成多少个手铐;
解题思路
由于本人过于太菜,就只会用tarjon来写,正解应该是书上的点分治我们先用tarjon缩完点之后构造出一幅新的无向无环图,然后再这棵树上找规律,
设 f[u]为u这个节点后面连了多少个环,
ans[x] 通过表示当前搜索范围中 x这个节点可以构造多少个手铐;
至于转移方程感觉还是个人画个图好好模拟一遍比较好直接看代码吧
注意的是,这个题我很迷的是一个代码第一次过了又叫一次过了一半数据又交了一半过了70%再交有ac了,这是为啥呢?233
//
// Created by ZHY on 2020/5/12.
//
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=19260817;
const int MX=2001000;
ll n,m,cnt,sum;
int color[MX],color_num[MX];
vector<int>ve[MX],vec[MX];
int dfn[MX],low[MX];
ll ans[MX],f[MX];
stack<int>st;
struct node{
int x,y;
} edge[MX];
///**********************这个题好像卡输入输出这里加了个快读*********************
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
///***********************tarjon缩点*************************
void tarjon(int x,int fa)
{
dfn[x]=low[x]=++cnt;
st.push(x);
for(int i:ve[x])
{
if(i!=fa){
if(!dfn[i])
{
tarjon(i,x);
low[x]=min(low[i],low[x]);
}else {
low[x]=min(low[x],dfn[i]);
}
}
}
int k=0;
if(low[x]==dfn[x])
{
sum++;
do{
k=st.top();st.pop();
color[k]=sum;
color_num[sum]++;
}while(x!=k);
}
}
void dfs(int x,int fa){
ans[x]=0;
if(color_num[x]>1){
f[x]=1;
}else f[x]=0;
for(int i: vec[x])
{
if(i!=fa){
dfs(i,x);
///******************************转移方程**********************************************
ans[x]=(ans[x]+ans[i]+f[x]*f[i])%mod;
if(color_num[x]>1) f[x]=(f[x]+f[i]*2)%mod;
else f[x]=(f[x]+f[i])%mod;
}
}
}
int main()
{
n=read();
m=read();
for(int i=0,x,y;i<m;i++)
{
x=read();
y=read();
edge[i].x=x;
edge[i].y=y;
ve[x].push_back(y);
ve[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i]) tarjon(i,0);
}
///*************************构造一幅新图***************************************
for(int i=0;i<m;i++)
{
int x=edge[i].x;
int y=edge[i].y;
int u=color[x];
int v=color[y];
if(v!=u){
vec[u].push_back(v);
vec[v].push_back(u);
}
}
dfs(1,0);
printf("%lld\n",ans[1]);
return 0;
}