Noip模拟题解题报告

Pro

题目链接

Sol

公交车

这道题,要么点多要么边多。

正解是点多,因为我们要在公交线路上的任意两点间连一条边,这样就会产生很多的边,但是我们可以加一个虚点,向虚点连边就少很多,因为上了公交车就付一次费,所以只需要一条边有权值就好。

然后就是水的dij优化最短路。

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#define INF 1e18
using namespace std;

struct Node {
	long long to , next , len;
};
Node e[1000005];
struct Que {
	long long num , len;
	Que(long long nn , long long dd) {
		num = nn , len = dd;
	}
	bool operator < (const Que &a) const {
		return len > a.len;
	}
};
long long n , m , k , s , tot , head[200005] , dis[200005] , z[200005];

void add(long long x , long long y , long long z) {
	tot++;
	e[tot].next = head[x];
	e[tot].to = y;
	e[tot].len = z;
	head[x] = tot;
}

void dij(long long x) {
	priority_queue<Que>q;
	for(long long i=1; i<=n; i++)
		dis[i] = INF;
	dis[x] = 0;
	q.push(Que(x,dis[x]));
	while(!q.empty()) {
		Que u = q.top();
		q.pop();
		if(u.len != dis[u.num])
			continue;
		for(long long i=head[u.num]; i; i=e[i].next)
			if(dis[e[i].to] > u.len + e[i].len) {
				dis[e[i].to] = u.len + e[i].len;
				q.push(Que(e[i].to , dis[e[i].to]));
			}
	}
}

int main() {
	freopen("transprt.in","r",stdin);
	freopen("transprt.out","w",stdout);
	scanf("%lld%lld%lld%lld",&n,&m,&k,&s);
	for(long long i=1; i<=m; i++) {
		long long x , y , z;
		scanf("%lld%lld%lld",&x,&y,&z);
		add(x , y , z);
		add(y , x , z);
	}
	for(long long i=1; i<=k; i++) {
		long long x , y , z;
		scanf("%lld%lld",&x,&y);
		for(long long j=1; j<=y; j++) {
			scanf("%lld",&z);
			add(z , n+i , x);
			add(n+i , z , 0);
		}
	}
	n += k;
	dij(s);
	for(long long i=1; i<=n-k; i++)
		printf("%lld ",dis[i]);
	return 0;
}

灌溉

话说出题人真是的……两道题用到的思想差不多。

建一个虚点,虚点到各个点的距离为建水库的代价,问题就转化为:求最便宜的引水花费……

裸的MST,直接克鲁斯卡尔……

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define INF 2147483647
using namespace std;

struct Road {
	int d1 , d2 , len;
	bool operator < (const Road &a) const {
		return len < a.len;
	}
};
Road v[500005];
int n , top , val[305] , P[305][305] , fa[305] , ans;
inline int mymin(int a , int b) { return a<b?a:b; }

int find(int x) {
	if(fa[x]!=x)
		return find(fa[x]);
	return fa[x];
} 

void klus() {
	int cnt = 0;
	for(int i=1; i<=top; i++) {
		int fx = find(v[i].d1) , fy = find(v[i].d2);
		if(fx != fy) {
			cnt++;
			fa[fy] = fx;
			ans += v[i].len;
			if(cnt == n)
				return ;
		}
	}
}

void init() {
	for(int i=1; i<=n; i++)
		for(int j=i+1; j<=n; j++) {
			top++;
			v[top].d1 = i;
			v[top].d2 = j;
			v[top].len = P[i][j];		
		} 
	for(int i=1; i<=n; i++)
		fa[i] = i;
	sort(v+1 , v+top+1);
}

int main() {
	freopen("irrigate.in","r",stdin);
	freopen("irrigate.out","w",stdout);
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		int x;
		scanf("%d",&x);
		top++;
		v[top].d1 = i;
		v[top].d2 = n+1;
		v[top].len = x;
	}
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			scanf("%d",&P[i][j]);
	init();
	klus();
	printf("%d",ans);
	return 0;
}

对决

emmm,最简单的dfs……

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

struct Node {
	int to , next;
};
Node e[200005];
int n , m , head[100005] , tot , cdu[100005] , flag[100005][3] , opt[100005] , cnt , num[100005];

void add(int x , int y) {
	tot++;
	e[tot].next = head[x];
	e[tot].to = y;
	head[x] = tot;
}

void dfs(int u , int f) {
	if(opt[u]) {
		flag[f][opt[u]]++;
		return ;
	}
	for(int i=head[u]; i; i=e[i].next)
		dfs(e[i].to , u);
	if(flag[u][1])
		opt[u] = 2;
	else
		opt[u] = 1;
	flag[f][opt[u]]++;
}

int main() {
	freopen("duel.in","r",stdin);
	freopen("duel.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++) {
		int x , y;
		scanf("%d%d",&x,&y);
		add(x , y);
		cdu[x]++;
	}
	for(int i=1; i<=n; i++)
		if(!cdu[i])
			num[++cnt] = i;
	sort(num+1 , num+cnt+1);
	for(int i=1; i<=cnt; i++) {
		int x;
		scanf("%d",&x);
		opt[num[i]] = x + 1;
	}
	for(int i=1; i<=n; i++)
		if(!opt[i])
			dfs(i,0);
	for(int i=1; i<=n; i++) {
		if(opt[i]==1)
			printf("Last\n");
		else
			printf("First\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值