2023冲刺国赛模拟17

最近的题题解咕了好多,看着补吧。。。

A. 掌分治

直接按照连通块考虑没啥前途,根据期望的线型性,把贡献看成点对的贡献

\(f_{i, j}\) 表示当 \(i\) 为 根时, \(j\) 在其所在连通块的概率

求总和即为答案

考虑实际上限制的是 \(i\)\(i - j\) 路径上第一个删掉的点,那么概率就是路径上点数分之一

考虑环如何容斥,其实就是顺时针联通+逆时针联通-全联通

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int mod = 998244353;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
const int maxn = 405;
int n, m, inv[maxn]; 
vector<int>g[maxn + maxn], e[maxn];
int dfn[maxn], low[maxn], tim, st[maxn], top, cnt;
int f[maxn][maxn];
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
void dfs(int x){
	dfn[x] = low[x] = ++tim;
	st[++top] = x;
	for(int v : e[x]){
		if(dfn[v])low[x] = min(low[x], dfn[v]);
		else{
			dfs(v); low[x] = min(low[x], low[v]);
			if(low[v] == dfn[x]){
				++cnt; int y;
				do{
					y = st[top--];
					g[cnt].push_back(y);
					g[y].push_back(cnt);
				}while(y != v);
				g[cnt].push_back(x);
				g[x].push_back(cnt);
			}
		}
	}
}
void solve(int x, int fa){
	if(x <= n){
		for(int v : g[x])if(v != fa)solve(v, x);
		return;
	}
	int pf = 0; while(g[x][pf] != fa)++pf;
	for(int i = (pf + 1) % g[x].size(), a = 1; i != pf; i = (i + 1) % g[x].size(), ++a){
		int b = g[x].size() - a, v = g[x][i];
		for(int j = a + 1; j <= n; ++j)add(f[v][j], f[fa][j - a]);
		for(int j = b + 1; j <= n; ++j)add(f[v][j], f[fa][j - b]);
		for(int j = a + b; j <= n; ++j)add(f[v][j], mod - f[fa][j - a - b + 1]);
		solve(v, x);
	}
}
int main(){
	freopen("cactus.in","r",stdin);
	freopen("cactus.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		e[u].push_back(v); e[v].push_back(u);	
	}
	inv[1] = 1; for(int i = 2; i <= n; ++i)inv[i] = (mod - 1ll * mod / i * inv[mod % i] % mod) % mod;
	cnt = n; dfs(1); int ans = 0;
	for(int i = 1; i <= n; ++i){
		memset(f, 0, sizeof(f));
		f[i][1] = 1; solve(i, 0);
		for(int x = 1; x <= n; ++x)
			for(int j = 1; j <= n; ++j)
				ans = (ans + 1ll * f[x][j] * inv[j]) % mod;
	}
	printf("%d\n",ans);
	return 0;
}

B. 图圈圈

你说得对,但是随机化是一款ZYM大佬自主研发的.....

通过证明得到 \(m >= n + 2 \sqrt n\) 时一定为 \(yes\)

然后考虑对剩下的边建立一棵生成树,对非树边涉及到的点建立虚树

然后以所有点为根都暴力 \(DFS\) 每次判断目标点能走回根节点才走

判断可以之际暴力做,这样最终复杂度是 \(n^2\)

但是我显然写的不是这个,针对数据比较水,大部分可以暴力 \(DFS\) 搜索次数大于阈值直接无解

否则随机建树,每次考虑加上非树边构成的环,用随机\(hash\) 区分不同的环,可以通过本题。

数据但凡强点就没了。但是ZYM大佬巨

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 20005;
int n, m;
struct Edge{int u, v; ll val;}re[1000005], fe[1000005];
mt19937_64 rd((ull)&maxn);
struct DSU{
	int f[maxn];
	void init(){for(int i = 1; i <= n; ++i)f[i] = i;}
	int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
	bool merge(int x, int y){
		x = fa(x); y = fa(y);
		if(x == y)return false; 
		f[y] = x; 
		return true;
	}
}S;
int head[maxn], tot;
struct edge{int to, net; ll val;}e[maxn << 1];
void add(int u, int v, ll w){
	e[++tot] = {v, head[u], w};
	head[u] = tot;
}
int dep[maxn], fa[maxn], son[maxn], top[maxn], si[maxn];
ll sum[maxn], rlen[maxn];
void dfs1(int x){
	si[x] = 1; son[x] = 0;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x])continue;
		fa[v] = x; dep[v] = dep[x] + 1; sum[v] = sum[x] ^ e[i].val;
		dfs1(v); si[x] += si[v];
		if(si[son[x]] < si[v])son[x] = v;
	}	
}
void dfs2(int x, int tp){
	top[x] = tp;
	if(son[x])dfs2(son[x], tp);
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v != fa[x] && v != son[x])dfs2(v, v);
	}
}
void clear(){
	for(int i = 1; i <= n; ++i)head[i] = 0;
	for(int i = 1; i <= n; ++i)dep[i] = 0;
	tot = 0;
}
int LCA(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] > dep[top[v]])u = fa[top[u]];
		else v = fa[top[v]];
	}
	return dep[u] > dep[v] ? v : u;
}
void solve(){
	shuffle(re + 1, re + m + 1, rd);
	S.init(); int cnt = 0;
	for(int i = 1; i <= m; ++i)if(S.merge(re[i].u, re[i].v)){
		add(re[i].u, re[i].v, re[i].val);
		add(re[i].v, re[i].u, re[i].val);
	}else fe[++cnt] = re[i];
	for(int i = 1; i <= n; ++i)if(!dep[i]){
		dep[i] = 1; fa[i] = 0; sum[i] = 0; dfs1(i); dfs2(i, i);
	}
	for(int i = 1; i <= cnt; ++i){
		int lca = LCA(fe[i].u, fe[i].v);
		int len = dep[fe[i].u] + dep[fe[i].v] - 2 * dep[lca];
		ll val = sum[fe[i].u] ^ sum[fe[i].v] ^ fe[i].val;
		if(rlen[len] && rlen[len] != val){
			printf("Yes\n"); exit(0);
		}
		rlen[len] = val;
	}
	clear();
}
vector<int>g[maxn];
int s, dtim, len, cl[maxn]; bool vis[maxn];
void dfs(int x, int fa){
	++dtim;
	if(dtim > 2e8){
		printf("No\n"); exit(0);
	}
	vis[x] = true;
	++len;
	for(int v : g[x]){
		++dtim;
		if(v != fa){
			if(v == s){
				++cl[len];
				if(cl[len] == 3 && len >= 2){
					printf("Yes\n"); exit(0);
				}
			}
			if(!vis[v])dfs(v, x);
		}
	}
	--len;
	vis[x] = false;
}
void bf(){
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	for(int i = 1; i <= n; ++i)vis[i] = true;
	for(s = 1; s <= n; ++s)dfs(s, s);
	printf("No\n"); exit(0);
}
int main(){
	// freopen("graph.in","r",stdin);
	// freopen("graph.out","w",stdout);
	n = read(); m = read();
	if(m > n + 2 * sqrt(n)){printf("Yes\n"); return 0;}
	if(n < 20000)bf();
	for(int i = 1; i <= m; ++i)re[i].u = read(), re[i].v = read(), re[i].val = rd();
	while(clock() < 1.9 * CLOCKS_PER_SEC)solve();
	printf("No\n");
	return 0;
}
a

C. 下大雨

\(woc\) 原 ! \(Delov\) 喜提大保底!

雨棚 \(a\)\(b\) 上则由 \(a\)\(b\) 连边,这样肯定得到一个 \(DAG\)

\(f_{i}\) 表示考虑若干个雨棚之后,在 \(i\) 处落雨的最小打孔数量

每个雨棚相当于对 \(f\) 按照从高到低取前/后缀 \(min\), 然后区间 \(+1\)

暴力干喜提 \(30 - 50 pts\)

发现复杂度在建图和 \(DP\)

对于建图可以扫描线,插入时向前驱后继连边

对于 \(DP\) 考虑维护差分,用两个 \(map\) 分别维护正/负差分值

对于区间 \(+1\) 可以直接干

区间取 \(min\) 以前缀为例,就是对于正数差分值扔掉,在后面按顺序找负数差分值消掉,区间内消完的话,将没消掉的部分放在区间后面

可以想想实际过程

后缀的话就是扔掉负差分

复杂度分析使用势能分析

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 5e5 + 55;
const double eps = 1e-8;
int L, R, n;
struct node{
	int x1, y1, x2, y2;
	double calc(int x){return y1 + (x - x1) * (double)(y2 - y1) / (double)(x2 - x1);}
	bool in(int x){return min(x1, x2) <= x && max(x1, x2) >= x;}
}d[maxn];
int tmp[maxn * 8], cnt;
int deg[maxn * 8]; vector<int>g[maxn * 8], rl[maxn * 8], rr[maxn * 8];
struct data{
	int id;
	friend bool operator < (const data &x, const data &y){
		if(x.id == y.id)return false;
		if(d[y.id].in(d[x.id].x1))return d[x.id].y1 - eps > d[y.id].calc(d[x.id].x1);
		else if(d[y.id].in(d[x.id].x2))return d[x.id].y2 - eps > d[y.id].calc(d[x.id].x2);
		else return d[x.id].calc(d[y.id].x1) - eps > d[y.id].y1; 
	}
};
set<data>s;
map<int, int>mp1, mp2;
int f[maxn * 8];
queue<int>q;
int main(){
	freopen("rain.in","r",stdin);
	freopen("rain.out","w",stdout);
	L = read() * 2, R = read() * 2; n = read();
	for(int i = 1; i <= n; ++i){
		d[i].x1 = read() * 2, d[i].y1 = read(), d[i].x2 = read() * 2, d[i].y2 = read();
		tmp[++cnt] = d[i].x1; tmp[++cnt] = d[i].x2;
		tmp[++cnt] = d[i].x1 + 1; tmp[++cnt] = d[i].x1 - 1; tmp[++cnt] = d[i].x2 + 1; tmp[++cnt] = d[i].x2 - 1; 
	}
	tmp[++cnt] = L; tmp[++cnt] = R; tmp[++cnt] = (L + R) / 2;
	sort(tmp + 1, tmp + cnt + 1); cnt = unique(tmp + 1, tmp + cnt + 1) - tmp - 1;
	for(int i = 1; i <= n; ++i){
		int a = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x1) - tmp, b = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x2) - tmp;
		rl[min(a, b)].push_back(i); 
		rr[max(a, b)].push_back(i);
	}
	for(int i = 1; i <= cnt; ++i){
		for(int v : rl[i]){
			s.insert(data{v});
			auto it = s.lower_bound(data{v});
			if(next(it) != s.end())g[v].push_back((*next(it)).id), ++deg[(*next(it)).id];
			if(it != s.begin())g[(*prev(it)).id].push_back(v), ++deg[v];
		}
		for(int v : rr[i])s.erase(data{v});
	}
	L = lower_bound(tmp + 1, tmp + cnt + 1, L) - tmp; R = lower_bound(tmp + 1, tmp + cnt + 1, R) - tmp;
	for(int i = 1; i <= n; ++i){
		d[i].x1 = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x1) - tmp;
        d[i].x2 = lower_bound(tmp + 1, tmp + cnt + 1, d[i].x2) - tmp;
	}
	for(int i = 1; i <= n; ++i)if(deg[i] == 0)q.push(i);
	mp1[0] = 0x3f3f3f3f; mp2[L] = 0x3f3f3f3f; mp1[R + 1] = 0x3f3f3f3f;
	while(!q.empty()){
		int now = q.front(); q.pop();
		for(int v : g[now]){--deg[v]; if(deg[v] == 0)q.push(v);}
		if(d[now].x1 < d[now].x2){
			int p = d[now].x2 - 1, sum = 0, q = p;
			while(true){
				auto it = mp2.upper_bound(p);
				if(it == mp2.begin())break;
				it = prev(it); if((*it).first <= d[now].x1)break;
				int ns = (*it).second; p = (*it).first; mp2.erase(it);
				while(ns > 0 && q > d[now].x1){
					it = mp1.upper_bound(min(p, q));
					if(it == mp1.begin())break;
					--it; if((*it).first <= d[now].x1)break;
					q = (*it).first;
					if((*it).second <= ns){
						ns -= (*it).second;
						mp1.erase(it);
					}else{
						mp1[q] -= ns;
						ns = 0;
					}
				}
				sum += ns;
			}
			if(sum){
				if(mp1.count(d[now].x1)){
					if(sum >= mp1[d[now].x1]){
						sum -= mp1[d[now].x1];
						mp1.erase(d[now].x1);
					}
				}
				if(sum)mp2[d[now].x1] += sum;
			}
			if(mp2.count(d[now].x1 + 1)){--mp2[d[now].x1 + 1]; if(mp2[d[now].x1 + 1] == 0)mp2.erase(d[now].x1 + 1);}
			else ++mp1[d[now].x1 + 1];
			if(mp1.count(d[now].x2 + 1)){--mp1[d[now].x2 + 1]; if(mp1[d[now].x2 + 1] == 0)mp1.erase(d[now].x2 + 1);}
			else ++mp2[d[now].x2 + 1];
		}else{
			int p = d[now].x2 + 1, sum = 0, q = p;
			while(true){
				auto it = mp1.lower_bound(p);
				if(it == mp1.end() || (*it).first > d[now].x1)break;
				int ns = (*it).second; p = (*it).first; mp1.erase(it);
				while(ns > 0 && q <= d[now].x1){
					it = mp2.lower_bound(max(q, p));
					if(it == mp2.end() || (*it).first > d[now].x1)break;
					q = (*it).first;
					if((*it).second <= ns){
						ns -= (*it).second;
						mp2.erase(it);
					}else{
						mp2[q] -= ns;
						ns = 0;
					}
				}
				sum += ns;
			}
			if(sum){
				if(mp2.count(d[now].x1 + 1)){
					if(sum >= mp2[d[now].x1 + 1]){
						sum -= mp2[d[now].x1 + 1];
						mp2.erase(d[now].x1 + 1);
					}
				}
				if(sum)mp1[d[now].x1 + 1] += sum;
			}
			if(mp2.count(d[now].x2)){--mp2[d[now].x2]; if(mp2[d[now].x2] == 0)mp2.erase(d[now].x2);}
			else ++mp1[d[now].x2];
			if(mp1.count(d[now].x1)){--mp1[d[now].x1]; if(mp1[d[now].x1] == 0)mp1.erase(d[now].x1);}
			else ++mp2[d[now].x1];
		}
	}
	int ans = 0x3f3f3f3f;
	for(auto it : mp1)f[it.first] += it.second;
	for(auto it : mp2)f[it.first] -= it.second;
	for(int i = 1; i <= cnt; ++i)f[i] += f[i - 1];
	for(int i = L; i <= R; ++i)ans = min(ans, f[i]);
	printf("%d\n",ans);
	return 0;
}
posted @ 2023-06-13 22:20  Chen_jr  阅读(33)  评论(1编辑  收藏  举报