solve : 5/11

补题 : 7/10


https://www.jisuanke.com/contest/3007?view=challenges

B. Dudu's maze

无向图,图上有糖果点和怪兽点,如果你经过怪兽点,你会被随机传送到一个与该怪兽点有边直接相连的点,但这种能力他只能使用一次,如果经过糖果点,他就能多收集一个糖果,现在在1号点有一个人(1号点一定是糖果点),假设这个人很聪明,问最多能收集到的糖果的期望是多少?

考虑将所有怪兽点去掉,那么整个图可能变成了多个连通块,考虑将一个联通块看成一个点,那么题意就变成了,他在第一个点上,每次遇到怪物点就随机走向一条边,求最后获得糖果期望的最大值

从1号点向下dfs,如果走到怪物点,那么他能获得的最大糖果数就是与怪兽点直接连边的点的各连通块糖果数的和/联通块的数量。

注意1号连通块我们直接去掉,所以在上一步需要特判是否是包含1号点的连通块

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 1e5 + 5;
struct edge
{
	int to;
	int nex;
}e[MAXN * 4];
int n, m, k;
int now_color;
int tot, head[MAXN];
bool vis[MAXN], monster[MAXN];
int color_cnt[MAXN], color[MAXN];
double ans;
void init()
{
	ans = 0;
	now_color = 1;
	tot = 1;
	memset(head, -1, sizeof(head));
	memset(color, 0, sizeof(color));
	memset(color_cnt, 0, sizeof(color_cnt));
	memset(vis, false, sizeof(vis));
	memset(monster, false, sizeof(monster));
}
void add(int a, int b)
{
	e[tot] = edge{ b,head[a] };
	head[a] = tot++;
}
void dfs(int u, int col)//染色,每个联通块染一种颜色
{
	for (int i = head[u]; i + 1; i = e[i].nex)
	{
		int to = e[i].to;
		if (monster[to] == false && vis[to] == false)
		{
			vis[to] = true;
			color[to] = col;
			color_cnt[col]++;
			dfs(to, col);
		}
	}
}
void dfs1(int u)//从1开始向下搜
{
	for (int i = head[u]; i + 1; i = e[i].nex)
	{
		int to = e[i].to;
		if (vis[to] == true)
			continue;
		vis[to] = true;
		if (monster[to])
		{
			double res = 0;
			int nums = 0;
			for (int j = head[to]; j + 1; j = e[j].nex)
			{
				int too = e[j].to;
				if (color[too] == color[1])//是包含 1号点 的联通块
					res += 0;
				else
					res += color_cnt[color[too]];
				nums++;
			}
			res /= nums;//有多少种情况
			ans = max(ans, res);
		}
		else
			dfs1(to);
	}
}
int main()
{
	int T;
	sc("%d", &T);
	while (T--)
	{
		init();
		sc("%d%d%d", &n, &m, &k);
		while (m--)
		{
			int a, b;
			sc("%d%d", &a, &b);
			add(a, b);
			add(b, a);
		}
		while (k--)
		{
			int a;
			sc("%d", &a);
			monster[a] = true;
		}
		for (int i = 1; i <= n; i++)
		{
			if (monster[i] == false && vis[i] == false)
			{
				vis[i] = true;
				color[i] = now_color;
				color_cnt[now_color]++;
				dfs(i, now_color);
				now_color++;
			}
		}
		memset(vis, false, sizeof(vis));
		vis[1] = true;
		dfs1(1);
		ans += color_cnt[color[1]];
		pr("%.6lf\n", ans);
	}
}

C. Dawn-K's water

复杂度看起来就很像01背包。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[20005];
int main()
{
	int n, m;
	while (~scanf("%d %d", &n, &m))
	{
		ll ans = 1e18, mx;
		memset(dp, 0x3f, sizeof(dp));
		ll p, c;
		dp[0] = 0;
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld %lld", &p, &c);
			for (int j = 0; j <= m; j++)
			{
				dp[j + c] = min(dp[j + c], dp[j] + p);
			}
		}
		for (int i = m; i <= 20000; i++)
		{
			if (ans >= dp[i])
			{
				ans = dp[i];
				mx = i;
			}
		}
		printf("%lld %lld\n", ans, mx);
	}
}

D. Fish eating fruit

给一棵树,n个点,求图上每个点到达除这个点外其他点的距离对 3 取余等于 0,1,2 的距离之和。

树形dp

1、考虑以 1 为根,自下向上跑一个以 i 为根的结点数和 i 到 子树内所有点的距离模3=0,1,2的距离和(dfs1

2、那么我们得到了 1 到剩下所有点的距离之和。

3、考虑将以 1 为根的状态转移到以 2 为根的状态(假设2是1的儿子

4、首先将 2 从 1 的子树中拿掉

5、然后按照dfs1的方法,1 认 2 做爸爸,然后将他们连起来。

6、一直向下递归就可以处理完,注意 1 可能有多棵子树,所以处理完一颗子树后,需要将父节点还原。

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
#define Pair pair<int,int>
using namespace std;
const int MAXN = 1e4 + 5;
const ll mod = 1e9 + 7;
const ll inf = 1e18 + 7;
struct edge
{
	int to;
	ll w;
	int nex;
}e[MAXN * 2];
int tot, head[MAXN];
int n, m;
ll ans[3], num[MAXN][3], dis[MAXN][3];
void init()
{
	for (int i = 0; i <= n; i++)
	{
		for (int j = 0; j < 3; j++)
			num[i][j] = dis[i][j] = ans[j] = 0;
	}
	tot = 1;
	memset(head, -1, sizeof(head));
}
void add(int in, int to, ll dis)
{
	e[tot] = edge{ to,dis,head[in] };
	head[in] = tot++;
}
void update(int u)
{
	for (int i = 0; i < 3; i++)
		ans[i] = (ans[i] + dis[u][i]) % mod;
}
void dfs1(int u, int fa, ll len)
{
	for (int i = head[u]; i + 1; i = e[i].nex)
	{
		int to = e[i].to;
		if (to == fa)
			continue;
		dfs1(to, u, len + e[i].w);
		num[to][0]++;
		for (int j = 0; j < 3; j++)
		{
			num[u][(e[i].w + j) % 3] = (num[u][(e[i].w + j) % 3] + num[to][j]) % mod;
			dis[u][(e[i].w + j) % 3] = (dis[u][(e[i].w + j) % 3] + dis[to][j] + e[i].w * (num[to][j])) % mod;
		}
	}
}
void dfs2(int u, int fa)
{
	for (int i = head[u]; i + 1; i = e[i].nex)
	{
		int to = e[i].to;
		if (to == fa)
			continue;
		ll disu[3], numu[3], disto[3], numto[3];
		for (int j = 0; j < 3; j++)
			disu[j] = dis[u][j], numu[j] = num[u][j], disto[j] = dis[to][j], numto[j] = num[to][j];
		
		for (int j = 0; j < 3; j++)
		{
			dis[u][(j + e[i].w) % 3] = ((dis[u][(j + e[i].w) % 3] - (dis[to][j] + num[to][j] * e[i].w)) % mod + mod) % mod;
			num[u][(j + e[i].w) % 3] = (num[u][(j + e[i].w) % 3] - num[to][j] + mod) % mod;
		}
		num[to][0]--;
		num[u][0]++;
		for (int j = 0; j < 3; j++)
		{
			dis[to][(j + e[i].w) % 3] = ((dis[to][(j + e[i].w) % 3] + (dis[u][j] + num[u][j] * e[i].w)) % mod + mod) % mod;
			num[to][(j + e[i].w) % 3] = (num[to][(j + e[i].w) % 3] + num[u][j]) % mod;
		}

		update(to); 
		//pr("%lld %lld %lld\n", ans[0], ans[1], ans[2]);
		dfs2(to, u);
		for (int j = 0; j < 3; j++)
			dis[u][j]= disu[j], num[u][j] = numu[j], dis[to][j] = disto[j], num[to][j] = numto[j];
	}
}
int main()
{
	while (sc("%d", &n) > 0)
	{
		m = n - 1;
		init();
		while (m--)
		{
			int a, b; ll c;
			sc("%d%d%lld", &a, &b, &c);
			a++; b++;
			add(a, b, c);
			add(b, a, c);
		}
		dfs1(1, 0, 0);
		update(1);
		dfs2(1, 0);
		pr("%lld %lld %lld\n", ans[0], ans[1], ans[2]);
	}
}
/*
6
0 1 1
1 2 1
2 3 1
0 5 1
1 4 1
*/

F. Honk's pool

有 n 桶水,m次操作,每桶水的高度已知,每次操作可以将一桶的高度1倒入另一桶水,求最高的水与最低的水的高度差的最小值。

1、m次操作,意味着可以下降m高度,可以上升m高度,所以我们可以先算出他最小值的最大值和最大值的最小值。

2、若最小值的最大值<最大值的最小值,说明不可能将他们完全放平,所以直接输出最大值与最小值的差值就可以。

3、反之,说明有可能可以放平,那么我们只需要判一下水的总量%桶数量,就可以知道是否可以放平。

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 5e5 + 5;
ll a[MAXN];
int main()
{
	ll n, m, sum = 0;
	while (sc("%lld%lld", &n, &m) > 0)
	{
		sum = 0;
		for (int i = 1; i <= n; i++)
		{
			sc("%lld", &a[i]);
			sum += a[i];
		}
		sort(a + 1, a + n + 1);
		ll pos1 = a[1], pos2 = a[n];
		ll ans = 0;
		for (ll i = 2; i <= n; i++)
		{
			if (ans + (a[i] - a[i - 1]) * (i - 1) <= m)
				pos1 = a[i];
			else
			{
				pos1 = a[i - 1] + (m - ans) / (i - 1);
				break;
			}
			ans += (a[i] - a[i - 1]) * (i - 1);
		}
		ans = 0;
		for (ll i = n - 1; i >= 1; i--)
		{
			if (ans + (a[i + 1] - a[i]) * (n - i) <= m)
				pos2 = a[i];
			else
			{
				pos2 = a[i + 1] - (m - ans) / (n - i);
				break;
			}
			ans += (a[i + 1] - a[i]) * (n - i);
		}
		if (pos1 < pos2)
			pr("%lld\n", pos2 - pos1);
		else
		{
			if (sum % n == 0)
				pr("0\n");
			else
				pr("1\n");
		}
	}
}

G. Special necklace

1、比较容易的电路图化简,可以比赛的时候没时间看,不然感觉能A。

2、并联电阻 ,所以 

3、由题意,这个三角形任意两个顶点的阻值是 2a ,所以  , 

4、然后大概就是求出一个的情况,然后疯狂递归,然后,这玩意越后面影响就越小,所以如果输入比较大的话,保留最后两位数字来算就可以了

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
char s[1000005];
int main()
{
	int T;
	double a;
	sc("%d", &T);
	while (T--)
	{
		sc("%s%lf", s, &a);
		int n = 0;
		int len = strlen(s);
		if (len >= 3)
			n = 100;
		else if (len == 2)
			n = (s[0] - '0') * 10 + s[1];
		else
			n = s[0] - '0';
		n--;
		double ans = 5.0 / 3.0;
		while (n--)
		{
			double temp = (ans * 3.0 / 4.0) / (ans + 3.0 / 4.0) + 3;
			ans = temp * 3 / (temp + 3);
		}
		pr("%.10lf\n", ans * a);
	}
}

H. Texas hold'em Poker

大模拟,求求出题人大模拟找人验一下吧,自己觉得什么都说清楚了,问就是No response。

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
struct node
{
	char name[15];
	int type;
	int t1;
	int t2;
	int t3;
}in[100005];
char s[15];
int a[15];
int main()
{
	int n;
	while (sc("%d", &n) > 0)
	{
		for (int i = 0; i < n; i++)
		{
			sc("%s", in[i].name);
			for (int j = 1; j <= 13; j++)
				a[j] = 0;
			in[i].t1 = in[i].t2 = in[i].t3 = 0;
			sc("%s", s);
			int len = strlen(s);
			for (int j = 0; j < len; j++)
			{
				if (s[j] == 'A')
					a[1]++;
				else if (s[j] == 'J')
					a[11]++;
				else if (s[j] == 'Q')
					a[12]++;
				else if (s[j] == 'K')
					a[13]++;
				else if (s[j] == '1')
				{
					j++;
					a[10]++;
				}
				else
					a[s[j] - '0']++;
			}
			//1
			if (a[10] == 1 && a[11] == 1 && a[12] == 1 && a[13] == 1 && a[1] == 1)
				in[i].type = 1;
			else
			{
				//2 have t1
				for (int j = 1; j <= 9; j++)
				{
					if (a[j] == 1 && a[j + 1] == 1 && a[j + 2] == 1 && a[j + 3] == 1 && a[j + 4] == 1)
					{
						in[i].type = 2;
						in[i].t1 = j + 4;
						goto qwe;
					}
				}
				//3 have t1 t2
				for (int j = 1; j <= 13; j++)
				{
					if (a[j] == 4)
					{
						for (int k = 1; k <= 13; k++)
						{
							if (a[k] == 1)
							{
								in[i].type = 3;
								in[i].t1 = j;
								in[i].t2 = k;
								goto qwe;
							}
						}
					}
				}
				//4 have t1 t2
				for (int j = 1; j <= 13; j++)
				{
					if (a[j] == 3)
					{
						for (int k = 1; k <= 13; k++)
						{
							if (a[k] == 2)
							{
								in[i].type = 4;
								in[i].t1 = j;
								in[i].t2 = k;//?
								goto qwe;
							}
						}
					}
				}
				//5
				for (int j = 1; j <= 13; j++)
				{
					if (a[j] == 3)
					{
						in[i].type = 5;
						in[i].t1 = j;
						for (int k = 1; k <= 13; k++)
						{
							if (a[k] == 1)
								in[i].t2 += k;
						}
						goto qwe;
					}
				}
				//6
				for (int j = 13; j >= 1; j--)
				{
					if (a[j] == 2)
					{
						for (int k = j - 1; k >= 1; k--)
						{
							if (a[k] == 2)
							{
								for (int l = 13; l >= 1; l--)
								{
									if (a[l] == 1)
									{
										in[i].type = 6;
										in[i].t1 = j;
										in[i].t2 = k;
										in[i].t3 = l;
										goto qwe;
									}
								}
							}
						}
					}
				}
				//7
				for (int j = 13; j >= 1; j--)
				{
					if (a[j] == 2)
					{
						in[i].type = 7;
						in[i].t1 = j;
						for (int k = 1; k <= 13; k++)
						{
							if (a[k] == 1)
								in[i].t2 += k;
						}
						goto qwe;
					}
				}
				//8
				in[i].type = 8;
				for (int j = 1; j <= 13; j++)
				{
					if (a[j] > 0)
						in[i].t1 += j * a[j];
				}
			}
			qwe:;
		}
		sort(in, in + n, [](node q, node w) {
			if (q.type != w.type)
				return q.type < w.type;
			if (q.t1 != w.t1)
				return q.t1 > w.t1;
			if (q.t2 != w.t2)
				return q.t2 > w.t2;
			if (q.t3 != w.t3)
				return q.t3 > w.t3;
			return strcmp(q.name, w.name) < 0;
			});
		for (int i = 0; i < n; i++)
			pr("%s\n", in[i].name);
	}
}

K. Guanguan's Happy water

不知道是数据问题还是数据本来就水

看了半天没看懂题,然后提个问题,出题人回答给我的意思就是我喝完水马上放新的水回去,然后取的概率还不变,然后猜了一手概率不变,莽过去了。

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const ll mod = 1e9 + 7;
ll a[100], f[100]; 
int main()
{
	int T;
	sc("%d", &T);
	while (T--)
	{
		int k;
		ll n;
		sc("%d%lld", &k, &n);
		ll ans = 0;
		for (int i = 1; i <= k; i++)
			sc("%lld", &a[i]);
		for (int i = 1; i <= k; i++)
			sc("%lld", &f[i]);
		if (n <= k)
		{
			for (int i = 1; i <= n; i++)
				ans = (ans + a[i]) % mod;
			pr("%lld\n", ans);
		}
		else
		{
			for (int i = 1; i <= k; i++)
				ans = (ans + a[i]) % mod;
			ans += (n - k) % mod * (f[1] % mod) % mod;
			ans %= mod;
			pr("%lld\n", ans);
		}
	}
}