codeforces1394B Boboniu Walks on Graph

本文详细解析了Codeforces竞赛中的一道题目B,探讨了如何通过枚举边(u,v,l)并利用set来表示不同选择之间的互斥关系,进而解决所有点能够走到自己的问题。通过DFS遍历和记录每种选择下点的入度,确保最终方案的可行性。

https://codeforces.com/contest/1394/problem/B

我们知道对于C的任意一组选择,最后每个点都只有一个出度,然而这题要保证最后所有点可以走到自己,那么这等价于每个点都只有一个出度和一个入度。

然后我们枚举每条边(u,v,l),把{out[u],rank(l)}给v,表示如果最后选择为c[out[u]]=rank(l),那么v这个点将会多一个入度,显然对于一个点v,所有这些选择之间必须选择一个,但不能选择多个,因为必须保证入度为1,所以他们是互斥了,用一个set表示每一个c[i]=j与哪些c[i2]=j2互斥

然后dfs的时候边选择边把当前选择互斥的那些点标记上,而且还要记录一下每个选择可以让多少点入度为1,要保证所有点都能入度为1.

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;

const int maxl=3e5+10;

int n,m,cas,k,cnt,tot,ans;
int a[maxl],out[maxl],c[maxl];
struct ed{int u,v,l;};
vector<ed> e[maxl];
ll in[100][100];int num[100][100];
typedef pair<int,int> p;
set <p> s[10][10];
vector<p> b[maxl];

inline bool cmp(const ed &a,const ed &b)
{
	return a.l<b.l;
}

inline void prework()
{
	scanf("%d%d%d",&n,&m,&k);
	int u,v,l;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&l);
		out[u]++;
		e[u].push_back(ed{u,v,l});
	}
	for(int i=1;i<=n;i++)
		sort(e[i].begin(),e[i].end(),cmp);
 	for(int i=1;i<=n;i++)
	{
		l=e[i].size();
		for(int j=0;j<l;j++)
			b[e[i][j].v].push_back({out[e[i][j].u],j+1});
	}
} 

inline void dfs(int x,int res)
{
	if(x>k)
	{
		if(res==0)
			ans++;
		return;
	}
	if(c[x]>0)
	{
		if(!in[x][c[x]])
		{
			a[x]=c[x];
			for(p d:s[x][c[x]])
				++in[d.first][d.second];
			dfs(x+1,res-num[x][c[x]]);
			a[x]=0;
			for(p d:s[x][c[x]])
				--in[d.first][d.second];
		}
	}
	else
	{
		for(int i=1;i<=x;i++)
		if(!in[x][i])
		{
			a[x]=i;
			for(p d:s[x][i])
				++in[d.first][d.second];
			dfs(x+1,res-num[x][i]);
			for(p d:s[x][i])
				--in[d.first][d.second];
			a[x]=0;
		}
	}
}

inline void mainwork()
{
	int u,v,l;ans=0;
	for(int i=1;i<=n;i++)
	{
		l=b[i].size();
		if(l==0)
			return;
		else if(l==1)
		{
			u=b[i][0].first;num[u][b[i][0].second]++;
			if(c[u]==0)
				c[b[i][0].first]=b[i][0].second;
			else if(c[u]!=b[i][0].second)
				return;
		}
		else
		{
			sort(b[i].begin(),b[i].end());
			for(int j=0;j<l;j++)
			if(j>1 && b[i][j]==b[i][j-1])
				in[b[i][j].first][b[i][j].second]++;
			b[i].erase(unique(b[i].begin(),b[i].end()),b[i].end());
			l=b[i].size();
			for(int j=0;j<l;j++)
			{
				num[b[i][j].first][b[i][j].second]++;
				for(int k=0;k<l;k++)
				if(j!=k)
				{
					u=b[i][j].first;
					v=b[i][k].first;
					s[u][b[i][j].second].insert(b[i][k]);
					s[v][b[i][k].second].insert(b[i][j]);
				}
			}
		}
	}
	dfs(1,n);
}

inline void print()
{
	printf("%d\n",ans);
}

int main()
{
	int t=1;
	//scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值