模拟赛精题

#21 T3:mst 

题意:给n(1~100000)个点,m条边(n-1<=m<=min(n*n-1,1e5),保证能够形成一棵树

求解:对于每一条边,求出每一条边是否在所有最小生成树上(any),或者至少在一颗最小生成树上(at least one),或者不在任何一棵树上(none)

思路:将权值排序,对所有权值相同的边一起操作。如果两条边在同一个连通分量,该边为none。

构造一个新图G1,将所有边对应的连通分量连边,然后求桥,所有的桥即为any,剩下的所有边为at least one

直到目前连边的数量超过n-1次,退出循环。  下面贴代码:

#include<stdio.h>
#include<algorithm>
#define N 100111
struct edge{   //原边
	int a, b, v, id;
	bool operator <(const edge& x)const{return v<x.v;}
}e[N];
struct edge2{  //新图的边
	int d, to, id;
}E[2*N];
int len, fa[N], pd[N], fi[N], vis[N], ans[N];
int first[N], low[N], dfn[N], num, Id;   //建新图的数组
int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void add(int a, int b, int c){
	E[++num].d=b;E[num].to=first[a];E[num].id=c;first[a]=num;
	E[++num].d=a;E[num].to=first[b];E[num].id=c;first[b]=num;
} 
void dfs(int u, int id){
	vis[u]=1;
	low[u]=dfn[u]=++Id;
	for(int i=first[u];i;i=E[i].to){
		int d=E[i].d;
		if(!vis[d]){
			dfs(d,E[i].id);
			if(dfn[u]<low[d]) ans[E[i].id]=1;
			if(low[d]<low[u]) low[u]=low[d];
		} else if(vis[d]&&id!=E[i].id&&dfn[d]<low[u]) low[u]=dfn[d];
	} vis[u]=0;
}
int tarjan(int l, int r){
	len=num=Id=0;   //记得每次清0
	for(int i=l;i<=r;i++){
		int f1=find(e[i].a), f2=find(e[i].b);
		if(f1==f2){
			ans[e[i].id]=-1;
			continue;
		} if(!pd[f1]) fi[++len]=f1,pd[f1]=1;
		if(!pd[f2]) fi[++len]=f2,pd[f2]=1;
		add(f1,f2,e[i].id);
	} for(int i=1;i<=len;i++) if(!dfn[fi[i]]) dfs(fi[i],0);
	for(int i=1;i<=len;i++) dfn[fi[i]]=low[fi[i]]=0;
	int cnt=0;                                       //下面记得把之前用过的连边清0,否则会WA
	for(int i=l;i<=r;i++){
		int f1=find(e[i].a), f2=find(e[i].b);
		pd[f1]=pd[f2]=first[f1]=first[f2]=0;
		if(f1!=f2) fa[f2]=f1,cnt++;
	} return cnt;
}
int main(){
	freopen("mst.in","r",stdin);
	freopen("mst.out","w",stdout);
	int n, m, l=1, r=1;
	scanf("%d%d", &n, &m);
	for(int i=1;i<=m;i++){
		scanf("%d %d %d", &e[i].a, &e[i].b, &e[i].v);
		e[i].id=i;
	} std::sort(e+1,e+m+1); e[m+1].v=-1;
	for(int i=1;i<=n;i++) fa[i]=i;
	int cnt=0;
	while(l<=m){
		while(e[r+1].v==e[r].v&&r<=m) r++;
		tarjan(l,r);
		if(cnt>=n) break;
		l=r=r+1;
	} for(int i=1;i<=m;i++)
	if(ans[i]==-1) puts("none");
	else if(ans[i]) puts("any");
	else puts("at least one");
	return 0;
}


#41 T2 part

题意:给你n个人,把所有人分成两队,对任意甲同学,必须使得与甲同学同队的人都愿意和甲同学同队,无解输出no solution

思路:二分图染色 将所有不能同队的同学连一条边,无法形成二分图则无解

有解时,对于每一个连通块,得到块内0和1的数量,然后进行背包求解

#include<cstdio>
#include<queue>
#define N 2011
using namespace std;
inline int read(){
	int t=getchar(), p=0;
	while(t<'0'||t>'9') t=getchar();
	while(t>='0'&&t<='9') p=p*10+t-48, t=getchar();
	return p;
}
struct edge{
	int d, to;
}e[N*N];
int num=0, first[N];
inline void add(int a, int b){
	e[++num].d=b;e[num].to=first[a];first[a]=num;
}
int n, cnt=0, co[N], g[N][N], dp[N], c1[N][2];
queue<int>s;
int bfs(int u){
	while(!s.empty()) s.pop();
	s.push(u);c1[cnt][1]++;
	while(!s.empty()){
		int x=s.front();s.pop();
		for(int i=first[x];i!=-1;i=e[i].to){
			int d=e[i].d;
			if(co[d]==-1){
				co[d]=!co[x];
				s.push(d);
				c1[cnt][co[d]]++;
			} else if(co[d]==co[x]) return 1;
		}
	} return 0;
}
int pre(){
	for(int i=1;i<=n;i++)
	if(co[i]==-1) {
		cnt++;co[i]=1;
		if(bfs(i)) return 1;
	} return 0;
}
int main(){
	freopen("part.in","r",stdin);
	freopen("part.out","w",stdout);
	int x;n=read();
	for(int i=1;i<=n;i++){
		while(1){
			x=read();
			if(!x) break;
			g[i][x]=1;
		} first[i]=-1,co[i]=-1;
	} for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++) if(g[i][j]==0&&i!=j) add(i,j),add(j,i);
	if(pre()){
		puts("No solution");
		return 0;
	} dp[0]=1;
	int m=n/2;
	for(int i=1;i<=cnt;i++)
	for(int j=m;j>0;j--){
		if(j>=c1[i][1]) dp[j]|=dp[j-c1[i][1]];
		if(j>=c1[i][0]) dp[j]|=dp[j-c1[i][0]];
	} for(int j=m;j>0;j--){
		if(dp[j]){
			printf("%d %d\n", j, n-j);
			return 0;
		}
	} return 0;
}

#41 T3 kobe

给一个图,求起点s走k次到终点t的最小距离

思路:快速幂

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 211
#define inf 1047480000
using namespace std;
inline int read(){
	int t=getchar(), p=0, f=1;
	while(t<'0'||t>'9'){
		if(t=='-') f=-1;
		t=getchar();
	}
	while(t>='0'&&t<='9') p=p*10+t-48, t=getchar();
	return p*f;
}
int n, S, T, K;
void min(int& x, int y){
	if(x>y) x=y;
}
struct Mat{
	int g[N][N];
	Mat operator *(Mat& b){
		Mat c;int i, j, k;
		for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
		for(c.g[i][j]=inf,k=1;k<=n;k++) min(c.g[i][j],g[i][k]+b.g[k][j]);
		return c;
	}
}f;
int main(){
	freopen("kobe.in","r",stdin);
	freopen("kobe.out","w",stdout);
	n=read(), S=read(), T=read(), K=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++){
		f.g[i][j]=read();
		if(f.g[i][j]==-1) f.g[i][j]=inf;
	} Mat ans=f; K--;
	while(K){
		if(K&1) ans=ans*f;
		K>>=1; f=f*f;
	} if(ans.g[S][T]!=inf) printf("%d\n", ans.g[S][T]);
	else puts("-1");
//	printf("%d\n", inf);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值