基环树性质:
1.n点n边。
2.一般都是每个点必然有个出边,因为这样才能保证每个连通块都是一个环。
3.如果是一个连通块话的话,必然有一个环
4.对于找环的大小,如果无向图并且不能忽略重边,那么建立单向边并且用第一种类型dfs求环大小。如果有向图,那么强连通缩点求大小。
5.对于求环的某条边,一般求某一条边就够了,用并查集求。
题意:
就是给你一个n点n边的图,然后你可以让每条边定一个方向,使得这个图没有环。问你方案数同时取模。
思考:
代码:
#include
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;
int T,n,m,k;
int va[N];
int siz[N],vis[N];
vector<int > v;
vector<int > e[N];
void dfs(int now,int p)
{
vis[now] = 1;
siz[now] = siz[p]+1;
for(auto spot:e[now])
{
if(!vis[spot]) dfs(spot,now);
else if(vis[spot]==1) v.pb(siz[now]-siz[spot]+1);
}
vis[now] = 2;
}
int ksm(int a,int b)
{
int sum = 1;
while(b)
{
if(b&1) sum = sum*a%mod;
a = a*a%mod;
b >>= 1;
}
return sum;
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>va[i];
e[va[i]].pb(i);
}
int ans = 1;
for(int i=1;i<=n;i++)
{
if(!vis[i]) dfs(i,0);
}
int sum = 0;
for(auto t:v)
{
sum += t;
ans = ans*(ksm(2,t)-2)%mod; //只有两种方式不行
}
ans = ans*ksm(2,n-sum)%mod; //剩下的随便
ans = (ans%mod+mod)%mod;
cout<<ans;
return 0;
}
无向图不忽略重边
e[a].pb(b)建立单向边
void dfs(int now,int p)
{
vis[now] = 1;
siz[now] = siz[p]+1;
for(auto spot:e[now])
{
if(!vis[spot]) dfs(spot,now);
else if(vis[spot]==1) v.pb(siz[now]-siz[spot]+1);
}
vis[now] = 2;
}
无向图忽略重边
e[a].pb(b);
e[b].pb(a);建立双向边
void dfs(int now,int p)
{
col[now] = 1;
siz[now] = siz[p]+1;
for(auto spot:e[now])
{
if(spot==p) continue;
if(!col[spot]) dfs(spot,now);
else if(col[spot]==1) v.pb(siz[now]-siz[spot]+1);
}
col[now] = 2;
}
有向图忽略重边:
e[a].pb(b)建立单向边
强连通缩点后,找出所有环的大小>=2的即可
题意:
就是给你n个点n条边,保证这个图联通,然后每个点有个人流量,小A想在某些点开店,但是一条边的两个端点不能同时开店。第i个点开店的带来的价值是va[i]*k,k是一个实数。现在问你小A最大可以获得的价值。
思考:
代码:
#include
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;
int T,n,m,k;
int va[N];
int acc[N];
int dp[N][2];
int A,B;
vector<int > e[N];
int find(int x)
{
if(x!=acc[x]) acc[x] = find(acc[x]);
return acc[x];
}
void dfs(int now,int p)
{
dp[now][1] = va[now];
for(auto spot:e[now])
{
if(spot==p) continue;
dfs(spot,now);
dp[now][1] += dp[spot][0];
dp[now][0] += max(dp[spot][1],dp[spot][0]);
}
}
int solve1()
{
int tp = va[B];
va[B] = -inf;
for(int i=1;i<=n;i++) dp[i][0] = dp[i][1] = 0;
dfs(A,0);
va[B] = tp;
return max(dp[A][0],dp[A][1]);
}
int solve2()
{
int tp = va[A];
va[A] = -inf;
for(int i=1;i<=n;i++) dp[i][0] = dp[i][1] = 0;
dfs(B,0);
va[A] = tp;
return max(dp[B][0],dp[B][1]);
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<=n;i++) acc[i] = i;
for(int i=1;i<=n;i++) cin>>va[i];
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;a++,b++;
int t1 = find(a),t2 = find(b);
if(t1==t2) //要去掉成环的那个条边
{
A = a,B = b;
continue;
}
acc[t1] = t2;
e[a].pb(b);
e[b].pb(a);
}
int maxn = 0;
maxn = max(maxn,solve1());
maxn = max(maxn,solve2());
db tp;cin>>tp;
db ans = maxn*tp;
printf("%.1lf",ans);
return 0;
}
题意:
就是给你n个骑士,每个骑士有个自己的能力值和他讨厌的人。现在让你选择一些骑士,这些骑士之间不能有自己讨厌的人。问你选出所有骑士的最大战斗力。
思考:
#include
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e6+10,M = 2010;
int T,n,m,k;
int va[N];
int acc[N];
int dp[N][2];
int vis[N],st[N],A,B;
vector<int > e[N];
int find(int x)
{
if(x!=acc[x]) acc[x] = find(acc[x]);
return acc[x];
}
void get(int now,int p)
{
vis[now] = 1;
if(st[now])
{
if(A==-1) A = now;
else B = now;
}
for(auto spot:e[now])
{
if(!vis[spot]) get(spot,now);
}
}
void dfs(int now,int p)
{
dp[now][0] = 0,dp[now][1] = va[now];
for(auto spot:e[now])
{
if(spot==p) continue;
dfs(spot,now);
dp[now][1] += dp[spot][0];
dp[now][0] += max(dp[spot][0],dp[spot][1]);
}
}
int solve1()
{
int tp = va[B];
va[B] = -inf;
dfs(A,0);
va[B] = tp;
return max(dp[A][0],dp[A][1]);
}
int solve2()
{
int tp = va[A];
va[A] = -inf;
dfs(B,0);
va[A] = tp;
return max(dp[B][0],dp[B][1]);
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<=n;i++) acc[i] = i;
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;
va[i] = a;
int t1 = find(i),t2 = find(b);
if(t1==t2)
{
st[i] = st[b] = 1;
continue;
}
acc[t1] = t2;
e[i].pb(b);
e[b].pb(i);
}
int ans = 0;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
A = -1,B = -1;
get(i,0);
ans += max(solve1(),solve2());
}
}
cout<<ans;
return 0;
}
ABC-F - Well-defined Path Queries on a Namori
题意:
就是给你一个n个点n条边的连通图,然后给你m次询问,问你a和b两个点之间的简单路径是否唯一。
思考:
#include
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;
int T,n,m,k;
int va[N];
int acc[N][25],dep[N],cnt=22;
int fa[N],vis[N],col[N],A,B;
vector<int > e[N];
int find(int x)
{
if(x!=fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
void dfs(int now,int p)
{
acc[now][0] = p;
dep[now] = dep[p]+1;
for(auto spot:e[now])
{
if(spot==p) continue;
dfs(spot,now);
}
}
void get(int now,int p,int best)
{
col[now] = best;
for(auto spot:e[now])
{
if(spot==p||vis[spot]) continue; //不能走是环上的点
get(spot,now,best);
}
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<=n;i++) fa[i] = i;
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;
int t1 = find(a),t2 = find(b);
if(t1==t2)
{
A = a,B = b;
continue;
}
fa[t1] = t2;
e[a].pb(b);
e[b].pb(a);
}
dfs(A,0);
int now = B;
while(now)
{
vis[now] = 1;
now = acc[now][0];
}
for(int i=1;i<=n;i++)
{
if(vis[i]) get(i,0,i); //给环上每个点的子树分配颜色
}
cin>>m;
while(m--)
{
int a,b;
cin>>a>>b;
if(col[a]!=col[b]) cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}