Ten graph questions of about 2000 difficulty of Codeforces Round 2

1.CF702E Analysis of Pathes in Functional Graph

题意:有一个 n n n 个点 n n n 条边的带权有向图(点编号 0 0 0 ~ n − 1 n-1 n1),每个点有且仅有一条出边,对于每个点 i i i求出由 i i i出发经过 k k k条边,这 k k k条边的权值最小值和权值和。

思路:倍增

由于k太巨大,硬跑是肯定不行的,然后每个点有且仅有一条出边,所以可以用倍增(

预处理的方式跟ST表差不多

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
int mm[maxn][40],f[maxn][40],son[maxn][40],n,m,k;
inline void query(int i){
	int ans=0;
	int res=k;
	int x=1e18;
	for(int j=36;j>=0;j--){
		if((1ll<<j)<=res){
			res-=(1ll<<j);
			ans+=f[i][j];
			x=min(x,mm[i][j]);
			i=son[i][j];
		}
	}
	printf("%lld %lld\n",ans,x);
}
signed main(){
	cin>>n>>k;
	for(int i=0;i<n;i++){
		cin>>son[i][0];
	}
	for(int i=0;i<n;i++){
		cin>>f[i][0];
		mm[i][0]=f[i][0];
	}
	for(int j=1;j<=36;j++){
		for(int i=0;i<n;i++){
			son[i][j]=son[son[i][j-1]][j-1];
			f[i][j]=f[i][j-1]+f[son[i][j-1]][j-1];
			mm[i][j]=min(mm[i][j-1],mm[son[i][j-1]][j-1]);
		}
	}
	for(int i=0;i<n;i++){
		query(i);
	}
}

2.CF242D Dispute

题意:题意:有 n n n 个计数器和 m m m 条电线,每个计数器都对应着一个额定值 a i a_i ai ,初始时所有计数器的值均为 0 0 0 ,现在你可以按若干个计数器上的按钮,每次按按钮可以使该计数器以及与该计数器通过电线相连的计数器的值都 + 1 +1 +1 ,每个计数器最多只能按一次。当你进行这样的操作若干次后,若有一个计数器上的值与该计数器的额定值一样,你就失败了。需要输出任意一种方案使你可以获胜。若无法获胜,输出 − 1 -1 1

思路:bfs

先把额定值为0的点丢进队列,然后跑bfs,最后再遍历一次判断一下是否合法

#include<bits/stdc++.h>
using namespace std;
constexpr int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
int n,m,k,cnt,a[maxn],now[maxn],vis[maxn];
vector<int>e[maxn],jl;
void bfs(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(!a[i])q.push(i);
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		if(now[u]!=a[u])continue;
		now[u]++;
		jl.push_back(u);
		cnt++;
		vis[u]=1;
		for(auto v:e[u]){
			now[v]++;
			if(now[v]==a[v]&&!vis[v]){
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(now[i]==a[i]){
			puts("-1");
			return;
		}
	}
	cout<<cnt<<endl;
	for(auto x:jl){
		cout<<x<<" ";
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for(int i=1;i<=n;i++)cin>>a[i];
	bfs();
} 

3.CF229C Triangles

题意:给定n(1<=n<=10 6) 个点组成的完全图,现在从原图中拿走m(0<=m<=106)条边到另一个平面上,问一共还能组成多少个三角形。

思路:正面算两个图中有多少三角形肯定是不行的,逆向思维

先计算出整张图一共有多少个三角形

标记每个点有多少条被取走的边,被取走的边*剩余的边=被破坏的三角形个数

每个被破坏的三角形会属于两个点,要/2

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
int n,m,du[maxn];

signed main(){
	cin>>n>>m;
	int ans=n*(n-1)/2*(n-2)/3;
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%lld%lld",&u,&v);
		du[u]++;
		du[v]++;
	}
	int d=0;
	for(int i=1;i<=n;i++)d+=du[i]*(n-1-du[i]);
	cout<<ans-d/2;
} 

咕咕咕了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值