//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.7.27 解题报告

2023.7.27 解题报告

T1

观察题面发现只要我们的子树大小大于 \(\frac{n}{2}\) 就是 “很有权力的点”。

因为每一个士兵不从属于自己,所以我们在比较的时候是要 \(-1\) 的。

题目没说是向上还是向下取整,我们可以用 \(\frac{n + 1}{2}\) 来避免这个情况。

一遍 DFS,复杂度 \(O(n)\)

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, siz[N], fa[N], head[N], cnt, ans;
struct sb{int u, v, next;}e[N];

inline void add(int u, int v)
{
	e[++ cnt] = {u, v, head[u]};
	head[u] = cnt;
}

inline void dfs(int x, int f)
{
	siz[x] = 1;
	for(int i = head[x]; i; i = e[i].next)
	{
		int v = e[i].v;
		if(v == f) continue;
		dfs(v, x);
		siz[x] += siz[v];
	}
	return ;
}

signed main()
{
//	freopen("ex_authority4.in", "r", stdin);
	n = read();
	for(int i = 2; i <= n; i ++)
	{
		fa[i] = read();
		add(fa[i], i);
	}
	dfs(1, 0);
	for(int i = 1; i <= n; i ++)
		if(siz[i] - 1 >= (n + 1) / 2) ans ++;
	cout << ans << endl;
	return 0;
}

T2

考试发现了个似乎正确的规律(?

\(S\) 里面的点从小到大拍个序,其中所有的点没有病毒的点,都取出来,然后所有的组合乘一下最小的阶乘和后面的部分,然后输出就好。

没有什么好的方法只能爆搜枚举每一个选还是不选。

#include <bits/stdc++.h>

#define P 998244353
#define int long long
#define N 1000100

using namespace std;

int n, m, fz, fm, a[N], ok[N], stk[N], top;
map<int, int> mp;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

inline int ksm(int a, int b)
{
	int res = 1;
	while(b)
	{
		if(b & 1) res = (res * a) % P;
		b >>= 1; a = (a * a) % P;
	}
	return res;
}

inline void dfs(int x, int sum)
{
	if(x == top + 1) return fz = (fz + sum) % P, void();
	dfs(x + 1, (sum * (stk[x] - 1) % P));
	dfs(x + 1, sum);
	return ;
}

signed main()
{
//	freopen("ex_virus3.in", "r", stdin);
	n = read(), m = read();
	ok[0] = ok[1] = 1;
	for(int i = 2; i <= n; i ++)
		ok[i] = ok[i - 1] * i % P;
	fm = ksm(ok[n - 1], P - 2);
	while(m --)
	{
		mp.clear();
		top = fz = 0;
		int k = read();
		for(int i = 1; i <= k; i ++)
			a[i] = read(), mp[a[i]] = 1;
		sort(a + 1, a + k + 1);
		int c1 = ok[a[1] - 1], c2 = (ok[n - 1] / ok[a[k] - 1]) % P;
		if(n - a[k] == 0) c2 = 1;
		for(int i = a[1] + 1; i < a[k]; i ++)
			if(!mp[i]) stk[++ top] = i;
		dfs(1, 1);
//		fz ++;
//		cout << c1 << "     " << c2 << endl;
		fz = (fz * (c1 * c2 % P)) % P;
//		cout << "top:" << stk[1] << endl;
//		cout << fz << endl;
		cout << ((fz * fm) % P) << endl;
	}
	return 0;
}

正解:

假如 \(S\) 中点从小到大依次为 \(x_{1}, x_{2},\cdots,x_{m}\),可以发现“\(x_{i}\)\(x_{i + 1}\) 的祖先”这些事件之间是互相独立的。

然后通过计算,发现 \(x_{i}\)\(x_{i+1}\) 的祖先的概率和 \(x_{i + 1}\) 具体是什么无关;

我们令 \(x_{i + 1}\) 一直跳父亲,直到编号小于等于 \(x_{i}\) 的那一次。因为父亲是等概率选取的,所以概率就是 \(\frac{1}{x_{i}}\).

同时,对于原题答案即为 \(\prod^{m-1}_{i = 1}\frac{1}{x_{i}}\).

总复杂度 \(O(n + \sum k_{i})\).

#include <bits/stdc++.h>

#define P 998244353
#define int long long
#define N 1000100
#define endl '\n'

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, m, a[N];

inline int ksm(int a, int b)
{
	int res = 1;
	while(b)
	{
		if(b & 1) res = (res * a) % P;
		b >>= 1, a = (a * a) % P;
	}
	return res;
}

signed main()
{
	n = read(), m = read();
	while(m --)
	{
		int k = read(), ans = 1;
		for(int i = 1; i <= k; i ++)
			a[i] = read();
		sort(a + 1, a + k + 1);
		for(int i = 1; i < k; i ++)
			ans = (ans * a[i]) % P;
		ans = ksm(ans, P - 2);
		cout << ans << endl;
	}
	return 0;
}

T3

猜测有解当且仅当每个连通块边数都是偶数,下面构造进行证明。

对于一般的图而言,我们建立 dfs 树,这样非树边只有返祖边。从深到浅考察每个点,考虑这个点与儿子的连边及其连出的非树边中还没有匹配的边的数量,如果为偶数则直接两两匹配,否则再加入这个点与父亲的连边然后两两匹配。由于除了根节点之外每个点都能用和父亲的连边调整奇偶性,且连通块一共有偶数条边,所以一定能完美匹配。

#include <bits/stdc++.h>

#define int long long
#define N 1000100
#define endl '\n'

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int head[N], cnt, n, m, u[N], v[N], ans[N], vis[N], usd[N];
struct edge {int v, w, next;} e[N];

inline void add(int u, int v, int w) {e[++ cnt] = {v, w, head[u] }, head[u] = cnt;}

inline void dfs(int u,int id)//id是边的编号
{
	vis[u] = 1;
	for(int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].v;
		if(!vis[v]) dfs(v, e[i].w);
	}
	vector <int> q;//存放当前来u点的边的士兵
	for(int i = head[u]; i; i = e[i].next)//遍历每一个边
		if(!usd[e[i].w] && e[i].w != id)//如果当前的边没有去往别的点,并且不是与父节点的边
			q.push_back(e[i].w), usd[e[i].w] = 1;//存进去,标记
	if(q.size() & 1)//如果是偶数
	{
		if(id == 0) puts("NO"), exit(0);//当前点是根节点就不行,退出
		usd[id] = 1;//标记被使用过
		q.push_back(id);//把当前与父节点的放进vector
	}
	for(int i : q) ans[i] = u;//遍历并标记
	return ;
}

signed main()
{
	n = read(), m = read();
	for(int i = 1; i <= m; i ++)
	{
		u[i] = read(), v[i] = read();
		add(u[i], v[i], i);
		add(v[i], u[i], i);
	}
	if(m & 1) return puts("0"), 0;
	for(int i = 1; i <= n; i ++)
		if(!vis[i])
			dfs(i, 0);
	puts("YES");
	for(int i = 1; i <= m; i ++)
		cout << ans[i] << endl;
	return 0;
}

T4

考试打了个暴力,被卡了。

枚举所有的右端点选或不选,然后暴力跑所有的区间查询。

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

map<int, int> mp, vis;
int n, m, k, stk[N], top, len, a[N], ans = 1e17;
struct sb{int l, r;}e[N];

inline int js()
{
	vis.clear();
	int res = 0;
	for(int i = 1; i <= m; i ++)
	{
		if(mp[e[i].r]) continue;
		for(int j = e[i].r; j >= e[i].l; j --)
		{
			if(mp[j]) break;
			if(vis[j]) continue;
			res += a[j];
			vis[j] = 1;
		}
	}
	return res;
}

inline void dfs(int x, int cnt)
{
	if(cnt > k || len - x + 1 < k - cnt) return ;
	if(x == len + 1 && cnt == k)
	{
		int res = js();
//		if(res < ans) cout << "res : " << res << endl;
		ans = min(ans, res);
		return ;
	}
	if(x > len) return ;
	dfs(x + 1, cnt);
	mp[stk[x]] = 1;
	dfs(x + 1, cnt + 1);
	mp[stk[x]] = 0;
	return ;
}

signed main()
{
//	freopen("ex_jgp3.in", "r", stdin);
	n = read(); m = read(); k = read();
	for(int i = 1; i <= n; i ++) a[i] = read();
	for(int i = 1; i <= m; i ++)
		e[i] = (sb){read(), read()}, stk[++ top] = e[i].r;
	sort(stk + 1, stk + top + 1);
	len = unique(stk + 1, stk + top + 1) - stk - 1;
	if(k >= len) return cout << "0" << endl, 0;
	dfs(1, 0);
	cout << ans << endl;
	return 0;
}

.

posted @ 2023-07-27 22:21  北烛青澜  阅读(1)  评论(0)    收藏  举报