2023 省选联测 22-40

2023 省选联测22

A. 因懒无名

结论是整个点集的最远点对是任意划分集合每部分最远点对中的点构成的

于是线段树维护即可

离线下来

对于每种颜色给他最大出现次数的位置个位置

注意如果查询到不存在的颜色可能需要特判

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 = 2e5 + 55;
int n, m, q, col[maxn];
vector<int>g[maxn];
int dep[maxn], son[maxn], fa[maxn], si[maxn], top[maxn];
void dfs1(int x){
	si[x] = 1;
	for(int v : g[x])if(v != fa[x]){
		fa[v] = x; dep[v] = dep[x] + 1;
		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 v : g[x])if(v != fa[x] && v != son[x])dfs2(v, v);
}
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
int Dis(int x, int y){
	if(x == 0 || y == 0)return -1;
	return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
struct node{
	int x[2], dis;
	node(){}
	node(int a, int b, int c){x[0] = a; x[1] = b; dis = c;}
	void init(int a){x[0] = x[1] = a; dis = a ? 0 : -1;}
	friend node operator + (const node &x, const node &y){
		int mx = max(x.dis, y.dis);
		for(int i = 0; i < 2; ++i)
			for(int j = 0; j < 2; ++j)
				mx = max(mx, Dis(x.x[i], y.x[j]));
		if(mx == x.dis)return x;
		if(mx == y.dis)return y;
		for(int i = 0; i < 2; ++i)
			for(int j = 0; j < 2; ++j)
				if(mx == Dis(x.x[i], y.x[j]))
					return node(x.x[i], y.x[j], Dis(x.x[i], y.x[j]));
	}
};
int id[maxn], sum;
struct seg{
	node t[maxn << 2 | 1];
	void build(int x, int l, int r){
		if(l == r){t[x].init(id[l]);return;}
		int mid = (l + r) >> 1; 
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}
	void modify(int x, int l, int r, int pos){
		if(l == r){t[x].init(id[l]);return;}
		int mid = (l + r) >> 1; 
		if(pos <= mid)modify(x << 1, l, mid, pos);
		else modify(x << 1 | 1, mid + 1, r, pos);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}
	node query(int x, int l, int r, int L, int R){
		if(L > R)return node(0, 0, -1);
		if(L <= l && r <= R)return t[x];
		int mid = (l + r) >> 1;
		if(L <= mid && R > mid)return query(x << 1, l, mid, L, R) + query(x << 1 | 1, mid + 1, r, L, R);
		if(L <= mid)return query(x << 1, l, mid, L, R);
		else return query(x << 1 | 1, mid + 1, r, L, R);
	}
}T;
int cmx[maxn], cnt[maxn], pl[maxn], pr[maxn], lcol[maxn], pos[maxn];
struct op{int opt, x, y;}d[maxn];
vector<int>rem[maxn];
int main(){
	freopen("noname.in","r",stdin);
	freopen("noname.out","w",stdout);
	n = read(); m = read(); q = read();
	for(int i = 1; i <= n; ++i)++cnt[col[i] = read()];
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	dfs1(1); dfs2(1, 1);
	for(int i = 1; i <= m; ++i)cmx[i] = cnt[i], lcol[i] = col[i];
	for(int i = 1; i <= q; ++i){
		d[i].opt = read(); d[i].x = read(); d[i].y = read();
		if(d[i].opt & 1){
			--cnt[lcol[d[i].x]]; ++cnt[d[i].y];
			lcol[d[i].x] = d[i].y; cmx[d[i].y] = max(cmx[d[i].y], cnt[d[i].y]);
		}
	}
	sum = 0;
	for(int i = 1; i <= m; ++i){
		pl[i] = sum + 1;
		sum += cmx[i];
		pr[i] = sum;
		for(int j = pl[i]; j <= pr[i]; ++j)rem[i].push_back(j);
	}
	for(int i = 1; i <= n; ++i){
		id[rem[col[i]].back()] = i;
		pos[i] = rem[col[i]].back(); rem[col[i]].pop_back();
	}
	T.build(1, 1, sum);
	for(int i = 1; i <= q; ++i){
		if(d[i].opt & 1){
			id[pos[d[i].x]] = 0; T.modify(1, 1, sum, pos[d[i].x]);
			rem[col[d[i].x]].push_back(pos[d[i].x]);
			col[d[i].x] = d[i].y;
			pos[d[i].x] = rem[col[d[i].x]].back(); 
			rem[col[d[i].x]].pop_back();
			id[pos[d[i].x]] = d[i].x; T.modify(1, 1, sum, pos[d[i].x]);
		}else{
			node ans = T.query(1, 1, sum, pl[d[i].x], pr[d[i].y]);
			printf("%d\n",max(0, ans.dis));
		}
	}
	return 0;
}

B. 树论

mex 是取整颗子树的 ,不难发现是到最远的叶子距离

整棵树的 sg 值是所有棋子所在点的 sg 值的异或和

所以只需要关注每个点棋子数量的奇偶性

可以发现一个点的 mex 只有两种取值,维护最远和不在同一子树的次远值

当根不在最远值子树方向时取最远,否则取次远

根据树的直径的性质,最远点必然是直径的一个端点

找到直径中点,以他为根观察整颗树

发现每个点的最远点是与之不在直径中点同一侧的那个端点

所以如果当前根是 rt, 那么只有直径中点到 rt 链上的点能取到最远值其他点都只能取到次远值

那么换根的操作就能维护了

考虑子树加和链加,实际上是对是否取值的状态翻转

用树剖可以维护

一个相对简便的方法是对每个点维护一个 “矩阵”

两个位置放最远次远值,另外两个位置放 0

那么每次操作就变成了翻转行列

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 = 2e5 + 55;
int n, m;
vector<int>e[maxn];
int root, rt;
int dep[maxn], son[maxn], fa[maxn], si[maxn], top[maxn], dfn[maxn], id[maxn], tim;
int rem[maxn], f[maxn][2], g[maxn];
void dfs1(int x){
	si[x] = 1;
	for(int v : e[x])if(v != fa[x]){
		fa[v] = x; dep[v] = dep[x] + 1;
		dfs1(v); si[x] += si[v];
		if(si[son[x]] < si[v])son[x] = v;
	}
}
void dfs2(int x, int tp){
	top[x] = tp; id[dfn[x] = ++tim] = x;
	if(son[x])dfs2(son[x], tp);
	for(int v : e[x])if(v != fa[x] && v != son[x])dfs2(v, v);
}
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
struct seg{
	struct node{
		int a[2][2], r, c;
		node(){a[0][0] = a[1][0] = a[0][1] = a[1][1] = r = c = 0;}
		void rr(){
			swap(a[0][0], a[1][0]);
			swap(a[0][1], a[1][1]);
			r ^= 1;
		}
		void rc(){
			swap(a[0][0], a[0][1]);
			swap(a[1][0], a[1][1]);
			c ^= 1;
		}
		friend node operator + (const node &x, const node &y){
			node ans;
			for(int i = 0; i < 2; ++i)
				for(int j = 0; j < 2; ++j)
					ans.a[i][j] = x.a[i][j] ^ y.a[i][j];
			return ans;
		}
	}t[maxn << 2 | 1];
	void push_down(int x){
		if(t[x].r){t[x << 1].rr(); t[x << 1 | 1].rr(); t[x].r ^= 1;}
		if(t[x].c){t[x << 1].rc(); t[x << 1 | 1].rc(); t[x].c ^= 1;}
	}
	void modify(int x, int l, int r, int pos, int v1, int v2){
		if(l == r){
			t[x].a[0][0] = v1; 
			t[x].a[1][0] = v2;
			t[x].a[0][1] = t[x].a[1][1] = 0;
			if(t[x].c)t[x].rc(), t[x].c ^= 1;
			return;
		}
		push_down(x); int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos, v1, v2);
		else modify(x << 1 | 1, mid + 1, r, pos, v1, v2);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}
	void modify_r(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].rr();
		push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)modify_r(x << 1, l, mid, L, R);
		if(R > mid)modify_r(x << 1 | 1, mid + 1, r, L, R);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}
	void modify_c(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].rc();
		push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)modify_c(x << 1, l, mid, L, R);
		if(R > mid)modify_c(x << 1 | 1, mid + 1, r, L, R);
		t[x] = t[x << 1] + t[x << 1 | 1];
	}

}T;
void revr(int x, int pos){
	while(top[x] != top[pos]){
		T.modify_r(1, 1, n, dfn[top[x]], dfn[x]);
		x = fa[top[x]];
	}
	T.modify_r(1, 1, n, dfn[pos], dfn[x]);
}
void revc(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		T.modify_c(1, 1, n, dfn[top[u]], dfn[u]);
		u = fa[top[u]];
	}
	if(dep[u] > dep[v])swap(u, v);
	T.modify_c(1, 1, n, dfn[u], dfn[v]);
}
int get_pos(int x, int pos){
	while(top[x] != top[pos]){
		x = top[x]; 
		if(fa[x] == pos)return x;
		x = fa[x];
	}
	return son[pos];
}
void getf(int x){
	for(int v : e[x])if(v != fa[x]){
		fa[v] = x; getf(v);
		if(f[v][0] + 1 > f[x][0])f[x][1] = f[x][0], f[x][0] = f[v][0] + 1, rem[x] = v;
		else if(f[v][0] + 1 > f[x][1])f[x][1] = f[v][0] + 1;
	}
}
void getg(int x){
	for(int v : e[x])if(v != fa[x]){
		if(v == rem[x])g[v] = max(g[x], f[x][1]) + 1;
		else g[v] = max(g[x], f[x][0]) + 1;
		getg(v);
	}
}
void getrt(int x){
	if(rt != root){
		revr(rt, get_pos(rt, root));
		T.modify(1, 1, n, dfn[root], f[root][0], f[root][1]);
	}
	rt = x;
	if(rt != root){
		int pos = get_pos(rt, root);
		revr(rt, pos);
		if(pos == rem[root])T.modify(1, 1, n, dfn[root], f[root][1], f[root][0]);
		else T.modify(1, 1, n, dfn[root], f[root][0], f[root][1]);
	}
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n = read(); m = read(); 
	if(n == 1){
		for(int i = 1; i <= m; ++i)printf("0\n");
		return 0;
	}
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		e[u].push_back(v); e[v].push_back(u);
	}
	getf(1); getg(1);
	for(int i = 1; i <= n; ++i){
		if(g[i] > f[i][0])f[i][1] = f[i][0], f[i][0] = g[i], rem[i] = fa[i];
		else if(g[i] > f[i][1])f[i][1] = g[i];
	}
	for(int i = 1; i <= n; ++i){
		if(f[i][0] + f[i][1] > f[rt][0] + f[rt][1])rt = i;
		else if(f[i][0] - f[i][1] < f[rt][0] - f[rt][1])rt = i;
	}
	root = rt; fa[rt] = 0; dfs1(rt); dfs2(rt, rt);
	for(int i = 1; i <= n; ++i)
		if(i != root)T.modify(1, 1, n, dfn[i], f[i][1], f[i][0]);
		else T.modify(1, 1, n, dfn[i], f[i][0], f[i][1]);
	getrt(1);
	for(int i = 1; i <= m; ++i){
		int opt = read();
		if(opt & 1){
			int x = read(), y = read(); revc(x, y);
		}else{
			int x = read();
			if(x == rt)T.modify_c(1, 1, n, 1, n);
			else if((dfn[x] >= dfn[rt] && dfn[x] + si[x] <= dfn[rt] + si[rt]) || dfn[x] + si[x] <= dfn[rt] || dfn[rt] + si[rt] <= dfn[x]){
				T.modify_c(1, 1, n, dfn[x], dfn[x] + si[x] - 1);
			} else{
				x = get_pos(rt, x);
				T.modify_c(1, 1, n, 1, dfn[x] - 1);
				if(dfn[x] + si[x] <= n)
					T.modify_c(1, 1, n, dfn[x] + si[x], n);
			}
		}
		int nrt = read();
		getrt(nrt);
		printf("%d\n", T.t[1].a[0][0]);
	}
	return 0;
}

C. 游戏

https://www.luogu.com.cn/problem/solution/CF1349D

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 = 1e7 + 55, 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;
}
int n, a[maxn], b[maxn], f[maxn], m, inv[maxn];
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)b[i] = read();
	for(int i = 1; i <= n; ++i)m += a[i];
	inv[1] = 1; for(int i = 2; i <= m; ++i)inv[i] = mod - 1ll * mod / i * inv[mod % i] % mod;
	for(int i = 1; i < m; ++i){
		f[i + 1] = (1ll * m * (n - 1) % mod * inv[m - i] % mod - n + 2ll + mod) % mod * f[i] % mod;
		f[i + 1] = (f[i + 1] - 1ll * (n - 1) * i % mod * inv[m - i] % mod * (f[i - 1] + 1) % mod + mod) % mod;
	}
	int ans = 0;
	for(int i = 1; i <= n; ++i)ans = (0ll + ans + f[a[i]] - f[b[i]]) % mod;
	ans = (ans % mod + mod) % mod; printf("%d\n",ans);
	return 0;
}

2023 省选联测24

被爆踩的天数++

A. 和为贵

考虑枚举答案,当 x 为答案时,区间内恰好有 k1 个大于他的数

从大到小枚举 x,用链表维护大于他的数的位置

枚举合法区间计算答案

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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;
ll ans;
int n, k, a[maxn], p[maxn], pre[maxn], nxt[maxn];
set<int>s;
int main(){
	freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout);
	n = read(); k = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)p[a[i]] = i;
	s.insert(0); s.insert(n + 1); nxt[0] = n + 1;
	for(int i = n; i >= 1; --i){
		auto it = s.lower_bound(p[i]);
		int pr = *it, pl = *--it;
		pre[pr] = nxt[pl] = p[i];
		pre[p[i]] = pl; nxt[p[i]] = pr;
		s.insert(p[i]);
		vector<int>l, r;
		for(int j = p[i]; l.size() <= k; j = pre[j]){
			l.push_back(j); if(j == 0)break;
		}
		for(int j = p[i]; r.size() <= k; j = nxt[j]){
			r.push_back(j); if(j == n + 1)break;
		}
		for(int x = 1; x <= k && x < l.size(); ++x){
			int y = k - x + 1;
			if(y < r.size()){
				ans += 1ll * (l[x - 1] - l[x]) * (r[y] - r[y - 1]) * i;
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}

B. 图一乐

并查集维护连通性

对每个点用另一个并查集维护联通的连续段最远右端点

简单思考复杂度是对的

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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;
int n, m;
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]);}
	void merge(int x, int y){x = fa(x); y = fa(y); if(x < y)swap(x, y); f[y] = x;}
}g, s;
bool check(int l, int r){
	l = s.fa(l);
	while(l < r){
		if(g.fa(l) == g.fa(l + 1)){
			s.merge(l, l + 1);
		}else return false;
		l = s.fa(l);
	}
	return true;
}
int main(){
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	n = read(); m = read(); g.init(); s.init();
	for(int i = 1; i <= m; ++i){
		int opt = read(), l = read(), r = read();
		if(opt & 1)g.merge(l, r);
		else{
			if(check(l, r))printf("zzy\n");
			else printf("orz\n");
		}
	}
	return 0;
}

C. 拱火

记了一个错误结论,废了半天劲寄掉了

考虑每条边的贡献,当且仅当子树内有奇数个标记点时该边有 1 的贡献

那么加入一个点就是链翻转

扫描线,枚举右端点,考虑所有左端点的答案,我们不关心答案位置,只需要总和,于是只需要关心个数

那么用线段树维护每条边奇偶性的端点数即可

需要支持区间加和区间两种值交换

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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 = 3e5 + 55;
int n, dfn[maxn], tim, top[maxn], fa[maxn], son[maxn], si[maxn], dep[maxn], id[maxn];
vector<int>g[maxn];
void dfs1(int x){
	si[x] = 1;
	for(int v : g[x])if(v != fa[x]){
		fa[v] = x; dep[v] = dep[x] + 1;
		dfs1(v); si[x] += si[v];
		if(si[son[x]] < si[v])son[x] = v;
	}
}
void dfs2(int x, int tp){
	id[dfn[x] = ++tim] = x; top[x] = tp;
	if(son[x])dfs2(son[x], tp);
	for(int v : g[x])if(v != son[x] && v != fa[x])dfs2(v, v);
}
struct seg{
	struct node{
		int flag; ll val[2], tag[2];
	}t[maxn << 2 | 1];
	void push_up(int x){
		t[x].val[0] = t[x << 1].val[0] + t[x << 1 | 1].val[0];
		t[x].val[1] = t[x << 1].val[1] + t[x << 1 | 1].val[1];
	}
	void upd(int x, int si, int op, int val){t[x].val[op] += si * val; t[x].tag[op] += val;}
	void flip(int x){swap(t[x].val[0], t[x].val[1]); swap(t[x].tag[0], t[x].tag[1]); t[x].flag ^= 1;}
	void push_down(int x, int l, int r){
		if(t[x].flag)flip(x << 1), flip(x << 1 | 1), t[x].flag = 0;
		int mid = (l + r) >> 1;
		if(t[x].tag[0])upd(x << 1, mid - l + 1, 0, t[x].tag[0]), upd(x << 1 | 1, r - mid, 0, t[x].tag[0]), t[x].tag[0] = 0;
		if(t[x].tag[1])upd(x << 1, mid - l + 1, 1, t[x].tag[1]), upd(x << 1 | 1, r - mid, 1, t[x].tag[1]), t[x].tag[1] = 0;
	}
	void modify(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return flip(x);
		push_down(x, l, r); int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R);
		push_up(x);
	}
}T[2];
void update(int x){
	while(x){
		T[0].modify(1, 1, n, dfn[top[x]], dfn[x]);
		T[1].modify(1, 1, n, dfn[top[x]], dfn[x]);
		x = fa[top[x]];
	}
}
int main(){
	freopen("fire.in","r",stdin);
	freopen("fire.out","w",stdout);
	n = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	dfs1(1); dfs2(1, 1);
	ll ans = 0;
	for(int i = 1; i <= n; ++i){
		T[i & 1].upd(1, n, 0, 1); update(i);
		ans += T[!(i & 1)].t[1].val[1];
	}
	printf("%lld\n",ans);
	return 0;
}

D. 卷积之王

考虑当前卷积 (a,b) 两个序列

按照最高位分成

a0,a1,b0,b1

发现 b0/b1a1 卷贡献位置相同

那么递归去卷 (a0,b0),(a0,b1)

然后令 b1=max(b1,b0) 再去与 a1

复杂度 T(n)=3T(n/2)+O(n)

image

大概 3k

code
#include<bits/stdc++.h>
using namespace std;
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;
}
void ckmx(int &x, int y){if(x < y)x = y;}
const int maxn = 131072;
int n, a[maxn], b[maxn], c[maxn], tmp[maxn];
void solve(int l1, int l2, int len){
	if(len <= 4){
		for(int i = l1; i < l1 + len; ++i)
			for(int j = l2; j < l2 + len; ++j)
				ckmx(c[i | j], a[i] + b[j]);
		return;
	}
	len >>= 1;
	solve(l1, l2, len); solve(l1, l2 + len, len);
	for(int i = 0; i < len; ++i)tmp[l2 + i] = b[i + len + l2];
	for(int i = 0; i < len; ++i)ckmx(b[i + len + l2], b[i + l2]);
	solve(l1 + len, l2 + len, len);
	for(int i = 0; i < len; ++i)b[i + len + l2] = tmp[l2 + i];
}
int main(){
	freopen("ztl.in","r",stdin);
	freopen("ztl.out","w",stdout);
	n = 1 << read();
	for(int i = 0; i < n; ++i)a[i] = read();
	for(int i = 0; i < n; ++i)b[i] = read();
	solve(0, 0, n);
	for(int i = 0; i < n; ++i)printf("%d ",c[i]); printf("\n");
	return 0;
}

2023 省选联测26

A. 排列(roast)

首先找到最高的位存在 0/1 那么可以把数划分成两个集合

最优解可以 Tire 树上查询

考虑构造方案

设答案为 ans,上一个选择的是 x

那么 ansx 可以选择,和 x 在同一集合的可以选择

其他的不行

选择一个数还需要进行 check ,因为在两个集合之间切换的数对必须够用才能构造出方案

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
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 = 3e5 + 55;
struct trie{
	int ch[maxn * 31][2], cnt = 1;
	void ins(int x){
		int now = 1;
		for(int i = 30; i >= 0; --i){
			if(!ch[now][(x >> i) & 1])ch[now][(x >> i) & 1] = ++cnt;
			now = ch[now][(x >> i) & 1];
		}
	}
	int ckmi(int x){
		int now = 1, ans = 0;
		for(int i = 30; i >= 0; --i){
			if(ch[now][(x >> i) & 1])now = ch[now][(x >> i) & 1];
			else now = ch[now][((x >> i) & 1) ^ 1], ans |= (1 << i);
		}
		return ans;
	}
}T;
int n, a[maxn], hib, tot, mi = INT_MAX, sum[2], ans[maxn], b[maxn], link[maxn], bl[maxn];
unordered_map<int, int>id;
vector<int>rem[maxn];
set<int>s[2];
bool check(int x){
	if(rem[x].size() >= 2)return true;
	int t = b[x] >> hib;
	if(s[t].size() == 1 || s[t ^ 1].empty())return true;
	return sum[t ^ 1] - (!rem[link[x]].empty()) > 0;
}
void del(int i){
	s[bl[i]].erase(i);
	rem[a[i]].pop_back();
	if(rem[a[i]].empty()){
		int tmp = (!rem[link[a[i]]].empty());
		sum[0] -= tmp; sum[1] -= tmp;
	}
}
int main(){
	freopen("roast.in","r",stdin);
	freopen("roast.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 30; i >= 0; --i){
		int cnt = 0;
		for(int j = 1; j <= n; ++j)cnt += ((a[j] >> i) & 1);
		if(cnt == n)
			for(int j = 1; j <= n; ++j)a[j] ^= (1 << i);
		else if(cnt){hib = i; break;}
	}
	for(int i = n; i >= 1; --i){
		if(!id[a[i]])id[a[i]] = ++tot;
		rem[id[a[i]]].push_back(i);
	}
	for(int i = 1; i <= n; ++i)if(!(a[i] >> hib))T.ins(a[i]), s[0].insert(i);
	for(int i = 1; i <= n; ++i)if((a[i] >> hib))mi = min(mi, T.ckmi(a[i])), s[1].insert(i);
	for(int i = 1; i <= n; ++i){
		bl[i] = a[i] >> hib;
		b[id[a[i]]] = a[i];
		a[i] = id[a[i]];
		link[a[i]] = id[b[a[i]] ^ mi];
		if(rem[a[i]].back() == i)sum[bl[i]] += (link[a[i]] != 0);
	}
	for(int i = 1; i <= n; ++i)if(check(a[i])){del(i); ans[1] = i; break;}	
	for(int i = 2; i <= n; ++i){
		int lidk = link[a[ans[i - 1]]], lbl = bl[ans[i - 1]];
		int x = n + 1, y = n + 1;
		for(auto it : s[lbl])if(check(a[it])){x = it; break;}
		if(!rem[lidk].empty() && check(a[rem[lidk].back()]))y = rem[lidk].back();
		x = min(x, y); del(x); ans[i] = x;
	}
	for(int i = 1; i <= n; ++i)printf("%d ",ans[i]); printf("\n");
	return 0;
}

B. 王(king)

把贡献转化一下

某种颜色的出现次数 + 1, 可以转化成在该种颜色中至多选择一个

连续段可以转化成选择一个

fi,j,0/1,0/1,0/1,0/1/2 表示到 (i,j) 红蓝选或没选,当前方向连续段选或没选,当前方向的贡献

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
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 = 2005;
const int mod = 998244353;
int n, m;
char col[maxn];
int v[maxn][maxn];
int f[2][maxn][2][2][2][3];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}

int main(){
	// freopen("king.in","r",stdin);
	// freopen("king.out","w",stdout);
	n = read(), m = read();
	scanf("%s",col + 1);
	for(int i = 1; i <= m; ++i)
		for(int j = 1; j <= n; ++j)
			v[i][j] = read();
	for(int i = 1; i <= n; ++i)
		for(int a = 0; a <= 1; ++a)
			for(int b = 0; b <= 1; ++b)
				if((a == 0 || col[i] == 'r') && (b == 0 || col[i] == 'b'))
					f[0][i][a][b][0][0] = v[1][i];
	for(int i = 2; i <= m; ++i){
		int to = (i & 1) ^ 1;
		for(int j = 1; j <= n; ++j)
			for(int a = 0; a <= 1; ++a)
				for(int b = 0; b <= 1; ++b)
					for(int s = 0; s <= 1; ++s)
						for(int d = 0; d <= 3; ++d)
							f[to][j][a][b][s][d] = 0;
		for(int j = 1; j <= n; ++j)
			for(int dir = -1; dir <= 1; ++dir){
				int nj = j + dir; if(nj == 0)nj = n; if(nj == n + 1)nj = 1;
				for(int a = 0; a <= 1; ++a)
					for(int b = 0; b <= 1; ++b)
						for(int d = 0; d <= 2; ++d)
							for(int s = (i == 2 || d == dir + 1) ? 0 : 1; s <= 1; ++s)
								for(int na = a; na <= max(a, (int)(col[nj] == 'r')); ++na)
									for(int nb = b; nb <= max(b, (int)(col[nj] == 'b')); ++nb)
										for(int ns = (s == 0 || d != dir + 1) ? 0 : 1; ns <= 1; ++ns)
											add(f[to][nj][na][nb][ns][dir + 1], 1ll * f[to ^ 1][j][a][b][s][d] * v[i][nj] % mod);
			}
	}
	int ans = 0;
	for(int i = 1; i <= n; ++i)
		for(int a = 0; a <= 1; ++a)
			for(int b = 0; b <= 1; ++b)
				for(int d = 0; d <= 2; ++d)
					add(ans, f[(m + 1) & 1][i][a][b][1][d]);
	printf("%d\n",ans);
	return 0;
}

C. 铲雪(snow)

image

50pts暴力
#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 = 2e5 + 55;
int n, q, w[maxn];
vector<pii>g[maxn];
int fa[maxn], dep[maxn];
ll val[maxn], ans;
void dfs(int x){
	for(auto v : g[x])if(v.first != fa[x]){
		fa[v.first] = x; dep[v.first] = dep[x] + 1;
		val[v.first] = v.second;
		dfs(v.first);
	}
}
ll rem[maxn], sum[maxn], mx[maxn];
void pre(int x){
	sum[x] = mx[x] = val[x];
	for(auto v : g[x])if(v.first != fa[x]){
		sum[x] += val[v.first]; mx[x] = max(mx[x], val[v.first]);
	}
	ll nval = 1ll * w[x] * max(sum[x] & 1, 2ll * mx[x] - sum[x]);
	ans = ans - rem[x] + nval; rem[x] = nval;
}
void upd(int u, int fu, int dt){
	val[u] += dt;
	sum[u] += dt; sum[fu] += dt;
	mx[u] = max(mx[u], val[u]);
	mx[fu] = max(mx[fu], val[u]);
	ans = ans - rem[u] - rem[fu];
	rem[u] = 1ll * w[u] * max(sum[u] & 1, 2ll * mx[u] - sum[u]);
	rem[fu] = 1ll * w[fu] * max(sum[fu] & 1, 2ll * mx[fu] - sum[fu]);
	ans = ans + rem[u] + rem[fu];
}
void add(int u, int v, int w){
	while(u != v){
		if(dep[u] < dep[v])swap(u, v);
		upd(u, fa[u], w);
		u = fa[u];
	}
}
int main(){
	freopen("snow.in","r",stdin);
	freopen("snow.out","w",stdout);
	n = read(), q = read();
	for(int i = 1; i <= n; ++i)w[i] = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read(), w = read();
		g[u].push_back(pii{v, w});
		g[v].push_back(pii{u, w});
	}
	dfs(1); 
	for(int i = 1; i <= n; ++i)pre(i);
	printf("%lld\n",ans);
	for(int i = 1; i <= q; ++i){
		int u = read(),v = read(), w = read();
		add(u, v, w);
		printf("%lld\n",ans);
	}
	return 0;
}

2023 省选联测27

A. 神奇纸牌(uno)

首先问题可以转化成问只有一个联通块的方案数

然后说两种做法,还有一种神奇容斥我没看

一种做法是设 fi,s 表示前 i 种牌连通性为 s 的方案数

预处理 s 只有 52

然后使用矩阵加速

另一种做法是考虑一种牌的每种颜色出现情况有 24

224 枚举每种 每种颜色出现情况 是否出现

然后暴力 check

问题就是 n 个不同的球划分成 m 个非空互异集合的方案数

=i=0m(1)miin(mi)

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

using namespace std;

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

const int maxn = 25;
ll n;
int mod, c[maxn][maxn], s[maxn], tot;
bool vis[maxn];
int qpow(ll x, ll y){
	x %= mod; ll ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * x * ans % mod;
	return ans;
}
void add(int &x, int y){x = (0ll + x + y) % mod;}
int f[maxn];
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
bool check(){
	tot = 0;
	for(int i = 0; i < 16; ++i)if(vis[i])s[++tot] = i;
	for(int i = 1; i <= tot; ++i)f[i] = i;
	for(int i = 1; i <= tot; ++i)
		for(int j = i + 1; j <= tot; ++j)
			if((s[i] & s[j]) || (!s[i]) || (!s[j]))f[fa(i)] = fa(j), s[i] |= s[j], s[j] |= s[i];
	//0存在,如果不或的话,那么就会连上错误的边
	int cnt = 0;
	for(int i = 1; i <= tot; ++i)cnt += (fa(i) == i);
	return cnt == 1;
}
int sol(){
	int m = 0; for(int i = 0; i < 16; ++i)m += vis[i];
	int ans = 0;
	for(int i = 0; i <= m; ++i){
		int res = 1ll * qpow(i, n) * c[m][i] % mod;
		if((m - i) & 1)res = mod - res;
		add(ans, res);
	}
	return ans;
}
int main(){
	// freopen("uno.in","r",stdin);
	// freopen("uno.out","w",stdout);	
	cin >> n >> mod;
	for(int i = 0; i <= 16; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= i; ++j)
			c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	}
	int ans = 0;
	for(int i = 0; i < (1 << 16); ++i){
		for(int j = 0; j < 16; ++j)vis[j] = ((i >> j) & 1);
		if(check())add(ans, sol());
	}
	printf("%d\n",ans);
	return 0;
}

B. 凌乱平衡树(treap)

问题可以转化成求新树所有点 size 之和

考虑对两棵树分别求解再计算变化量

每棵树的答案在 rotate 时可以简单维护

现在考虑合并产生的贡献

发现只会影响到左树的右链,右树的左链

反过来考虑原树 size 会产生的贡献,他会贡献到介于 size 和父亲 size 之间的另一棵树上的节点

使用值域线段树维护 size

rotate 时考虑如果 f 在目标链上则可能是等价于删去 x 也可能是加上 x,分类讨论即可

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 = 2e6 + 55;
ll ans;
struct seg{
	int t[maxn];
	void modify(int x, int l, int r, int pos, int val){
		t[x] += val; if(l == r)return;
		int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos, val);
		else modify(x << 1 | 1, mid + 1, r, pos, val);
	}
	int query_size(int x, int l, int r, int L, int R){
		if(L > R)return 0;
		if(L <= l && r <= R)return t[x];
		int mid = (l + r) >> 1, res = 0;
		if(L <= mid)res += query_size(x << 1, l, mid, L, R);
		if(R > mid)res += query_size(x << 1 | 1, mid + 1, r, L, R);
		return res;
	}
	int find_pre(int x, int l, int r, int val){
		if(val < l || !t[x])return 0;
		if(l == r)return l;
		int mid = (l + r) >> 1;
		if(val <= mid)return find_pre(x << 1, l, mid, val);
		int qr = find_pre(x << 1 | 1, mid + 1, r, val);
		return qr ? qr : find_pre(x << 1, l, mid , val);
	}
}T[2];
int n;
struct splay_treap{
	struct node{
		int son[2], fa, size;
	}t[maxn];
	int rt, si; bool is[maxn];
	int son(int x){return t[t[x].fa].son[1] == x;}
	void push_up(int x){t[x].size = t[t[x].son[0]].size + t[t[x].son[1]].size + 1;}
	void dfs(int x){
		if(t[x].son[0])dfs(t[x].son[0]);
		if(t[x].son[1])dfs(t[x].son[1]);
		push_up(x);
	}
	int calcL(int now){return 1ll * t[now].size * T[1].query_size(1, 1, n, t[now].size + 1, now == rt ? n : t[t[now].fa].size);}
	int calcR(int now){return 1ll * t[now].size * T[0].query_size(1, 1, n, t[now].size, now == rt ? n : t[t[now].fa].size - 1);}
	void init(int id){
		for(int i = 1; i <= si; ++i){
			t[i].son[0] = read(), t[i].son[1] = read();
			if(t[i].son[0])t[t[i].son[0]].fa = i;
			if(t[i].son[1])t[t[i].son[1]].fa = i;
		}
		for(int i = 1; i <= si; ++i)if(!t[i].fa)rt = i;
		dfs(rt); for(int i = 1; i <= si; ++i)ans += t[i].size;
		int now = rt; 
		while(now){
			is[now] = true;
			T[id].modify(1, 1, n, t[now].size, 1);
			now = t[now].son[id ^ 1];
		}
	}
	void rotateL(int x, int f, int s){
		if(!is[f])return;
		if(s){ 
			ans -= 1ll * (t[x].size - t[t[x].son[1]].size) * T[1].query_size(1, 1, n, t[x].size + 1, t[f].size) + T[1].find_pre(1, 1, n, t[x].size);
			T[0].modify(1, 1, n, t[x].size, -1); is[f] = false;
		}else{
			push_up(f); push_up(x);
			ans += 1ll * (t[f].size - t[t[f].son[1]].size) * T[1].query_size(1, 1, n, t[f].size + 1, t[x].size) + T[1].find_pre(1, 1, n, t[f].size);
			T[0].modify(1, 1, n, t[f].size, 1); is[x] = true;
		}
	}
	void rotateR(int x, int f, int s){
		if(!is[f])return;
		if(s){ 
			push_up(f); push_up(x);
			ans += 1ll * (t[f].size - t[t[f].son[0]].size) * T[0].query_size(1, 1, n, t[f].size, t[x].size - 1) + T[0].find_pre(1, 1, n, t[f].size - 1);
			T[1].modify(1, 1, n, t[f].size, 1); is[x] = true;
		}else{
			ans -= 1ll * (t[x].size - t[t[x].son[0]].size) * T[0].query_size(1, 1, n, t[x].size, t[f].size - 1) + T[0].find_pre(1, 1, n, t[x].size - 1);
			T[1].modify(1, 1, n, t[x].size, -1); is[f] = false;
		}
	}
	void rotate(int x, int type){
		int f = t[x].fa, ff = t[f].fa, s = son(x), ss = son(f);
		ans -= t[x].size + t[f].size;
		t[f].son[s]= t[x].son[s ^ 1];
		if(t[x].son[s ^ 1])t[t[x].son[s ^ 1]].fa = f;
		t[x].son[s ^ 1] = f; t[x].fa = ff; t[f].fa = x;
		if(ff)t[ff].son[ss] = x; else rt = x;
		if(type)rotateR(x, f, s); else rotateL(x, f, s);
		push_up(f); push_up(x); 
		ans += t[x].size + t[f].size;
	}
}L, R;
int main(){
	freopen("treap.in","r",stdin);
	freopen("treap.out","w",stdout);	
	L.si = read(); R.si = read(); 
	n = max(L.si, R.si);
	L.init(0); R.init(1);
	for(int now = L.rt; now; now = L.t[now].son[1])ans += L.calcL(now);
	for(int now = R.rt; now; now = R.t[now].son[0])ans += R.calcR(now);
	printf("%lld\n",ans);
	int q = read();
	for(int i = 1; i <= q; ++i){
		int op = read(), x = read();
		if(op & 1)L.rotate(x, 0);
		else R.rotate(x, 1);
		printf("%lld\n",ans);
	}
	return 0;
}

C. 打扫笛卡尔(cartesian)

代码异常简洁

fi,gi 表示由 i 个数构成的排列舒适度的期望和选中点数量的期望

枚举树根和左子树大小进行转移

因为树根必选,所以期望为 i!

走到子树内才有贡献,所以 ×12

fi=i!+j=0i1(i1j)[(fj+gj)×(i1j)!+(fi1j+gi1j)×j!]×12

类似的求 g

gi=i!+j=0i1(i1j)[gj×(i1j)!+gi1j×j!]×12

这个东西可以优化,由于方法相同,所以只写 f

fi=i!+j=0i1(i1j)[(fj+gj)×(i1j)!+(fi1j+gi1j)×j!]×12

=i!+j=0i1(i1j)(fj+gj)×(i1j)!

=i!+(i1)!j=0i1(fj+gj)j!

=i!+j=0i1(fj+gj)k=j+1i1k

考虑每次会多一个 k==i1 的贡献,于是可以前缀和优化掉

code
#include<bits/stdc++.h>
using namespace std;
long long n, mod, f, g, sfg, sg, fac = 1;
int main(){
	freopen("cartesian.in","r",stdin);
	freopen("cartesian.out","w",stdout);	
	cin >> n >> mod;
	for(int i = 1; i <= n; ++i){
		fac = fac * i % mod;
		f = (sfg + fac) % mod;
		g = (sg + fac) % mod;
		sfg = (sfg * i % mod + f + g) % mod;
		sg = (sg * i % mod + g) % mod;
	}
	cout << f << endl;
	return 0;
}

2023 省选联测28

A. 装备(equipment)

考场假做法过了,实际上很容易卡掉

不要脸的粘一下 chino 大佬的代码

就是 ai>bi 建边

带环的一定不能换,不带环的实际上是换根

因为贪心,所以根只会往子树内走

check 一下是否合法,是否会修改到高位,等

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
namespace mycode{
	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 = 1e5 + 55;
	int n, m, k, c[maxn], a[maxn], b[maxn];
	vector<int>ans;
	int rem[maxn], rdep[maxn];
	bool ban[maxn], in[maxn];
	void check(int x){
		if(rdep[x])return;
		if(x == 0)return;
		if(in[x] || ban[b[x]]){
			rdep[x] = 0x3f3f3f3f;
			return;
		}
		in[x] = true;
		check(rem[b[x]]);
		rdep[x] = rdep[rem[b[x]]] + 1;
		in[x] = false;
	}
	void rev(int x){
		if(x == 0)return;
		rev(rem[b[x]]);
		ans.push_back(x);
		rem[a[x]] = 0;
		rem[b[x]] = x;
		swap(a[x], b[x]);
		ban[a[x]] = true;
	}
	void sol(){
		// freopen("equipment.in","r",stdin);
		// freopen("equipment.out","w",stdout);
		n = read(), m = read(), k = read();
		for(int i = 1; i <= n; ++i)c[i] = read();
		for(int i = 1; i <= m; ++i)a[i] = read(), b[i] = read();
		for(int i = 1; i <= m; ++i)rem[a[i]] = i;
		for(int i = m; i >= 1; --i){
			if(!ban[b[i]] && c[b[i]] > c[a[i]]){
				check(i);
				if(rdep[i] <= k){
					rev(i); 
					k -= rdep[i];
				}
			}
			if(c[a[i]] != c[b[i]])ban[a[i]] = true;
		}
		printf("%d\n",(int)ans.size());
		for(int v : ans)printf("%d\n",v);
	}
};
namespace chino_ac_actually{
	const int max1 = 1e5;
	int n, m, k, c[max1 + 5], a[max1 + 5], b[max1 + 5], v[max1 + 5], id[max1 + 5], degree[max1 + 5];
	int vis[max1 + 5];
	bool circle[max1 + 5];
	int root[max1 + 5], cnt;
	int father[max1 + 5], deep[max1 + 5], belong[max1 + 5], in[max1 + 5], out[max1 + 5], dfs_clock;
	bool flag[max1 + 5];
	vector<int> opt, tmp;
	struct Node {
	    int next, v;
	} edge[max1 + 5];
	int head[max1 + 5], total;
	void Add(int u, int v) {
	    edge[++total].v = v;
	    edge[total].next = head[u];
	    head[u] = total;
	    return;
	}
	void Find_Circle(int now, int tim) {
	    vis[now] = tim;
	    if (v[now]) {
	        if (!vis[v[now]])
	            Find_Circle(v[now], tim);
	        else if (vis[v[now]] == tim)
	            circle[now] = true;
	        if (!circle[now])
	            circle[now] = circle[v[now]];
	    }
	    return;
	}
	void Dfs(int now, int depth, int color) {
	    deep[now] = depth;
	    belong[now] = color;
	    in[now] = ++dfs_clock;
	    for (int i = head[now]; i; i = edge[i].next) {
	        int v = edge[i].v;
	        father[v] = now;
	        Dfs(v, depth + 1, color);
	    }
	    out[now] = dfs_clock;
	    return;
	}
	void Update(int now) {
	    int save = now, target = root[belong[now]];
	    tmp.clear();
	    while (now != target) {
	        if (flag[now])
	            break;
	        tmp.push_back(id[now]);
	        --k;
	        now = father[now];
	    }
	    if (flag[now]) {
	        now = save;
	        while (!flag[now]) flag[now] = true, now = father[now], ++k;
	    } else {
	        root[belong[now]] = save;
	        while (!tmp.empty()) opt.push_back(tmp.back()), tmp.pop_back();
	    }
	    return;
	}
	void sol() {
	    freopen("equipment.in", "r", stdin);
	    freopen("equipment.out", "w", stdout);
	    scanf("%d%d%d", &n, &m, &k);
	    for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
	    for (int i = 1; i <= m; i++) {
	        scanf("%d%d", &a[i], &b[i]);
	        v[a[i]] = b[i], id[a[i]] = i, ++degree[b[i]];
	    }
	    for (int i = 1; i <= n; i++)
	        if (!degree[i])
	            Find_Circle(i, i);
	    for (int i = 1; i <= n; i++)
	        if (!vis[i])
	            circle[i] = true;
	    for (int i = 1; i <= n; i++)
	        if (!circle[i] && !v[i])
	            root[++cnt] = i;
	    for (int i = 1; i <= m; i++) Add(b[i], a[i]);
	    for (int i = 1; i <= cnt; i++) Dfs(root[i], 1, i);
	    for (int i = m; i >= 1; i--)
	        if (!circle[a[i]] && root[belong[a[i]]] != a[i] && in[a[i]] >= in[root[belong[a[i]]]] &&
	            in[a[i]] <= out[root[belong[a[i]]]] && deep[a[i]] - deep[root[belong[a[i]]]] <= k) {
	            if (c[b[i]] > c[a[i]])
	                Update(a[i]);
	            else if (c[b[i]] < c[a[i]])
	                flag[a[i]] = true;
	        }
	    cout << opt.size() << endl;
	    for (vector<int>::iterator it = opt.begin(); it != opt.end(); it++) printf("%d\n", *it);
	}

};
int main(){
	chino_ac_actually::sol();
	return 0;
}

hack 数据

	5 4 3
  5 4 3 2 1
	 2 1 
	 4 3 
	 3 2 
	 5 4 

B. 比赛(tournament)

fl,r,x 表示 [l,r] x 赢的概率

转移枚举一个断点,在两侧区间分别枚举胜者,除以区间长度1

发现 fl,r,x=fl,x,x×fx,r,x

于是 fl,r,1/0 表示区间左/右端点赢的概率

进行前缀和优化

fl,r,2=fl,k,1×fk+1,r,0

实际上是左右端点都赢的概率

发现我们该枚举断点变成直接枚举另外一个区间赢的人

code
// ubsan: undefined
// accoders
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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 = 505, 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;
}
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int n, a[maxn], win[maxn][maxn];
int f[maxn][maxn][3];
int main(){
	freopen("tournament.in","r",stdin);
	freopen("tournament.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			win[i][j] = 1ll * a[i] * qpow(a[i] + a[j], mod - 2) % mod;
	for(int i = 1; i <= n; ++i)f[i][i][0] = f[i][i][1] = 1;
	for(int len = 2; len <= n; ++len){
		int inv = qpow(len - 1, mod - 2);
		for(int l = 1, r = len; r <= n; ++l, ++r){
			for(int p = l; p < r; ++p)add(f[l][r][2], 1ll * f[l][p][0] * f[p + 1][r][1] % mod);
			for(int p = l; p <= r; ++p){
				add(f[l][r][0], 1ll * win[l][p] * f[l][p][2] % mod * f[p][r][0] % mod * inv % mod);
				add(f[l][r][1], 1ll * win[r][p] * f[p][r][2] % mod * f[l][p][1] % mod * inv % mod);	
			}
		}
	}
	for(int i = 1; i <= n; ++i)printf("%lld\n",1ll * f[1][i][1] * f[i][n][0] % mod);
	return 0;
}

C. 取石子游戏(nim)

image

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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 = (1 << 18) + 55, mod = 998244353, inv2 = (mod + 1) >> 1;
int n, m;
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
struct node{
	int x[25];
	node(){memset(x, 0, sizeof(x));}
	void clear(){memset(x, 0, sizeof(x));}
	friend node operator + (const node &x, const node &y){
		node res; 
		for(int i = 0; i <= m; ++i)res.x[i] = (x.x[i] + y.x[i]) % mod; 
		return res;
	}
	friend node operator - (const node &x, const node &y){
		node res; 
		for(int i = 0; i <= m; ++i)res.x[i] = (x.x[i] - y.x[i] + mod) % mod; 
		return res;
	}
	friend node operator * (const node &x, const node &y){
		node res; 
		for(int i = 0; i <= m; ++i)
			for(int j = 0; j <= m; ++j)
				add(res.x[(i + j) % (m + 1)], 1ll * x.x[i] * y.x[j] % mod);
		return res;
	}
	friend node operator * (const node &x, const int &y){
		node res;
		for(int i = 0; i <= m; ++i)res.x[i] = 1ll * x.x[i] * y % mod;
		return res;
	}
}f[25][maxn], tmp[maxn];
int deg;
void fwt(node f[], int opt){
	for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
		for(int i = 0; i < deg; i += l)
			for(int j = i; j < i + hl; ++j){
				node x = f[j], y = f[j + hl];
				f[j] = (x + y) * opt; f[j + hl] = (x - y) * opt;
			}
}
void fwt(int f[], int opt){
	for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
		for(int i = 0; i < deg; i += l)
			for(int j = i; j < i + hl; ++j){
				int x = f[j], y = f[j + hl];
				f[j] = 1ll * (x + y) * opt % mod; f[j + hl] = 1ll * (x - y + mod) * opt % mod;
			}
}
int cnt[25]; int g[maxn], h[maxn];
int main(){
	freopen("nim.in","r",stdin);
	freopen("nim.out","w",stdout);
	n = read(), m = read();
	deg = 1 << ((__lg(100000 / (m + 1))) + 1);
	for(int i = 1; i <= n; ++i)++f[i][0].x[0], fwt(f[i], 1);
	for(int i = 1; i <= n; ++i){
		int k = read(), col = read();
		++cnt[col];
		for(int j = 0; j < deg; ++j)tmp[j].clear();
		for(int j = 1; j <= k; ++j){
			int x = read();
			++tmp[x / (m + 1)].x[x % (m + 1)];
		}
		fwt(tmp, 1);
		for(int j = 0; j < deg; ++j)f[col][j] = f[col][j] * tmp[j];
	}
	for(int i = 1; i <= n; ++i)fwt(f[i], inv2);
	deg = 1 << 18; g[0] = 1; fwt(g, 1);	
	for(int t = 1; t <= n; ++t){
		if(cnt[t] == 0)continue;
		for(int i = 0; i < deg; ++i)h[i] = 0;
		for(int i = 0; i <= deg / (m + 1); ++i)
            for(int j = 0; j <= m; ++j)add(h[i * (m + 1) + j], f[t][i].x[j]);
		fwt(h, 1);
		for(int i = 0; i < deg; ++i)g[i] = 1ll * g[i] * h[i] % mod;
	}
	fwt(g, inv2);
	int ans = 0;
	for(int i = 1; i < deg; ++i)add(ans, g[i]);
	printf("%d\n",ans);
	return 0;
}

2023 省选联测29

A. Alice 和 Bob 双在玩游戏

发现石子之间不影响,分开考虑贡献

定义先手/后手赢为正数/负数,每到一个白点/黑点操作次数+1/-1

初值设为 0 然后拓扑得到所有点值,跑一个背包即可

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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 = 305, mod = 998244353;
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int n, m, col[maxn], deg[maxn], val[maxn];
vector<int>g[maxn];
queue<int>q;
unordered_map<int, int>f, tmp;
char s[maxn];

int main(){
	freopen("gametwice.in","r",stdin);
	freopen("gametwice.out","w",stdout);
	n = read(), m = read();
	scanf("%s",s + 1);
	for(int i = 1; i <= n; ++i)col[i] = s[i] == 'W';
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		g[v].push_back(u); ++deg[u];
	}
	for(int i = 1; i <= n; ++i)if(deg[i] == 0)q.push(i);
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int v : g[x]){
			--deg[v]; if(deg[v] == 0)q.push(v);
			if(col[v])val[v] = max(val[v], val[x] + 1);
			else val[v] = min(val[v], val[x] - 1); 
		}
	}
	f[0] = 1;
	for(int i = 1; i <= n; ++i){
		for(auto it : f){
			add(tmp[it.first], it.second);
			add(tmp[it.first + val[i]], it.second);
		}
		swap(tmp, f); tmp.clear();
	}
	int ans = 0;
	for(auto it : f)if(it.first > 0)add(ans, it.second);
	printf("%d\n",ans);
	return 0;
}

B. 时代的眼泪·SP

分块,考虑如何整块往上跳

vis 数组记录

如果一个点跳到另外一个点之前跳过的地方那么他一定不如那个点优,于是可以删掉

这样复杂度就对了。。

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 = 3e5 + 55, sq = 505;
int n, m, root, a[maxn];
vector<pii>g[maxn];
int fa[maxn][19];
ll dis[maxn];
void dfs(int x){
	for(auto v : g[x])if(v.first != fa[x][0]){
		fa[v.first][0] = x;
		dis[v.first] = dis[x] + v.second;
		dfs(v.first);
	}
}
int lg[maxn];
int wt;
int kfa(int x, int k){
	// return x;
	if(!k || x == root)return x;	
	++wt;
	int hbit = __lg(k);
	for (int i = 0; i <= hbit; ++i)
        if ((k >> i) & 1)
            x = fa[x][i];
	return x;
}
int vis[sq][maxn], up[sq], jump[maxn], len, bl[maxn];
ll res[sq];
struct DSU{
	int f[maxn], val[maxn];
	void init(){for(int i = 1; i <= n; ++i)f[i] = i, val[i] = i;}
	int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
	int ele(int x){return val[fa(x)];}
	void ck(int x){if(dis[a[x]] < dis[a[ele(x)]])val[fa(x)] = x;}
	void merge(int x, int y){
		x = fa(x); y = fa(y); if(x == y)return; f[y] = x; 
		val[x] = dis[a[val[x]]] < dis[a[val[y]]] ? val[x] : val[y];
	}
}S;
vector<int>e[sq];
void jump_one(int i){
	// return;
	if(!vis[bl[i]][a[i]])vis[bl[i]][a[i]] = i;
	else S.merge(vis[bl[i]][a[i]], i);
	S.ck(i); jump[i] = up[bl[i]]; 
	res[bl[i]] = min(res[bl[i]], dis[a[i]]);
}
void jump_block(int x){
	++up[x];
	for(int i = 0; i < e[x].size(); ++i){
		int v = S.ele(e[x][i]); if(jump[v] == up[x])continue;
		++jump[v];
		a[v] = fa[a[v]][0]; res[x] = min(res[x], dis[a[v]]);
		if(vis[x][a[v]]){
			S.merge(v, vis[x][a[v]]);
			swap(e[x][i], e[x][e[x].size() - 1]);
			e[x].pop_back(); --i;
		}
		else vis[x][a[v]] = v;
	}
}
void modify(int l, int r){
	if(bl[l] == bl[r]){
		for(int i = l; i <= r; ++i)	a[i] = kfa(a[i], up[bl[i]] - jump[i] + 1);
		for(int i = l; i <= r; ++i)jump_one(i);
	}else{
		for(int i = l; i <= bl[l] * len; ++i)a[i] = kfa(a[i], up[bl[i]] - jump[i] + 1);
		for(int i = l; i <= bl[l] * len; ++i)jump_one(i);
		
		for(int i = (bl[r] - 1) * len + 1; i <= r; ++i)jump_one(i);
		
		for(int i = bl[l] + 1; i < bl[r]; ++i)a[i] = kfa(a[i], up[bl[i]] - jump[i] + 1);
		for(int i = bl[l] + 1; i < bl[r]; ++i)jump_block(i);		
	}
}
ll query_one(int i){
	// return 0x3f;
	if(!vis[bl[i]][a[i]])vis[bl[i]][a[i]] = i;
	else S.merge(vis[bl[i]][a[i]], i);
	S.ck(i); jump[i] = up[bl[i]]; 
	res[bl[i]] = min(res[bl[i]], dis[a[i]]);
	return dis[a[i]];
}
ll query(int l, int r){
	ll ans = 0x3f3f3f3f3f3f3f3f;
	if(bl[l] == bl[r]){
		for(int i = l; i <= r; ++i)a[i] = kfa(a[i], up[bl[i]] - jump[i]);
		for(int i = l; i <= r; ++i)ans = min(ans, query_one(i));
	}else{
		for(int i = l; i <= bl[l] * len; ++i)a[i] = kfa(a[i], up[bl[i]] - jump[i]);
		for(int i = l; i <= bl[l] * len; ++i)ans = min(ans, query_one(i));
		
		for(int i = (bl[r] - 1) * len + 1; i <= r; ++i)ans = min(ans, query_one(i));
		
		for(int i = bl[l] + 1; i < bl[r]; ++i)a[i] = kfa(a[i], up[bl[i]] - jump[i]);
		for(int i = bl[l] + 1; i < bl[r]; ++i)ans = min(ans, res[i]);		
	}
	return ans;
}
int main(){
	// freopen("tearssp.in","r",stdin);
	// freopen("tearssp.out","w",stdout);
	n = read(), m = read(), root = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read(), w = read();
		g[u].push_back(pii(v, w)); 
		g[v].push_back(pii(u, w)); 
	}
	for(int i = 1; i <= n; ++i)a[i] = read();
	fa[root][0] = root; dfs(root);
	for(int i = 1; i <= 18; ++i)
		for(int j = 1; j <= n; ++j)
			fa[j][i] = fa[fa[j][i - 1]][i - 1];
	for(int i = 2; i <= n; i += i)lg[i] = lg[i >> 1] + 1;
	len = sqrt(n); S.init(); 
	for(int i = 1; i <= (n + len - 1) / len; ++i)res[i] = 0x3f3f3f3f3f3f3f3f;
	for(int i = 1; i <= n; ++i){
		bl[i] = (i + len - 1) / len;
		e[bl[i]].push_back(i);
		vis[bl[i]][a[i]] = i;
		res[bl[i]] = min(res[bl[i]], dis[a[i]]);
	}
	for(int i = 1; i <= m; ++i){
		int op = read(), l = read(), r = read();
		if(op & 1)modify(l, r);
		else  printf("%lld\n",query(l, r));
	}
	cerr << wt << endl;
	return 0;
}

C. 钩子·Plus

J

image

2023 省选联测31

A. 西克 (shik)

分两部分考虑

xlca 直接倍增

lcay 其实可以树剖反向倍增,这里用的是一种离线的可撤销并查集

并查集维护 col=x 的答案,经过一个点axbx 时,把 ax,bx 合并,答案为 bx

新开一个点表示 ax

这样,在 lca 处记录之前倍增出的颜色对应的并查集结点

y 处查询即可得到答案

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int Mxdt = 100000;
inline char gc(){
	static char buf[Mxdt], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, Mxdt, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read(){
	int s = 0, f = 0; char ch = gc();
	while (ch < '0' || ch > '9')f |= (ch == '-'), ch = gc();
	while (ch >= '0' && ch <= '9')s = (s << 3) + (s << 1) + (ch ^ 48), ch = gc();
	return f ? -s : s;
}
const int maxn = 2e6 + 55;
vector<int>g[maxn];
int n, q, a[maxn], b[maxn], fa[maxn], dep[maxn], tr[maxn][21];
int si[maxn], son[maxn], top[maxn];
uint s1, s2;
uint rnd(){
	s1 *= s2, s2 >>= s1 & 13, s2 -= s1, s1 ^= s2;
	return ((s1 - s2) & (s1 + s2)) ^ (s1 * s2 >> 4);
}
void data(){
	for(int i = 500001; i <= n; i++){
		a[i] = rnd() % n + 1;
		b[i] = a[rnd() % 500000 + 1];
		if(a[i] == b[i]){
			++a[i];
			if (a[i] > n) a[i] = 1;
		}
	}
}
int rem[maxn];
void dfs(int x){
	int tmp = rem[a[x]]; rem[a[x]] = x; tr[x][0] = rem[b[x]]; si[x] = 1;
	for(int i = 1; i <= 20; ++i)tr[x][i] = tr[tr[x][i - 1]][i - 1];
	for(int v : g[x]){
		dep[v] = dep[x] + 1;
		dfs(v); si[x] += si[v];
		if(si[son[x]] < si[v])son[x] = v;
	}
	rem[a[x]] = tmp;
}
void dfs2(int x, int tp){
	top[x] = tp;
	if(son[x])dfs2(son[x], tp);
	for(int v : g[x])if(v != son[x])dfs2(v, v);
}
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
int ans[maxn], node[maxn], col[maxn];
vector<int>rf[maxn], rq[maxn];
void pre(int i){
	int x = read(), y = read();
	int l = lca(x, y);
	for(int i = 20; i >= 0; --i)if(dep[tr[x][i]] >= dep[l])x = tr[x][i];
	col[i] = b[x];
	rf[l].push_back(i);
	rq[y].push_back(i);
}
struct DSU{
	int f[maxn + maxn], siz[maxn + maxn], v[maxn + maxn];
	void init(int mx){for(int i = 1; i <= mx; ++i)f[i] = i, siz[i] = 1, v[i] = i;}
	void New(int id, int vv){f[id] = id; siz[id] = 1; v[id] = vv;}
	int fa(int x){return f[x] == x ? x : fa(f[x]);}
	vector<int>delx, dely, lv;
	void merge(int x, int y, int nv){
		x = fa(x); y = fa(y);
		assert(x != y);
		if(si[x] < si[y])swap(x, y);
		lv.push_back(v[x]); 
		delx.push_back(x);
		dely.push_back(y);
		f[y] = x; siz[x] += siz[y];
		v[x] = nv;
	}
	void pop(){
		assert(delx.size());
		v[delx.back()] = lv.back();
		f[dely.back()] = dely.back();
		siz[delx.back()] -= siz[dely.back()];
		delx.pop_back(); dely.pop_back(); lv.pop_back();
	}
	int query(int x){return v[fa(x)];}
}S;
void solve(int x){
	for(int i : rf[x])node[i] = rem[col[i]];
	int tmp = rem[a[x]]; 
	S.merge(rem[a[x]], rem[b[x]], b[x]);
	rem[a[x]] = ++n;S.New(n, a[x]);
	for(int i : rq[x])ans[i] = S.query(node[i]);
	for(int v : g[x])if(v != fa[x])solve(v);
	S.pop();
	rem[a[x]] = tmp; --n;	
}
int main(){
	freopen("shik.in","r",stdin);
	freopen("shik.out","w",stdout);
	n = read(), q = read(), s1 = read(), s2 = read();
	for(int i = 1; i <= min(n, 500000); ++i)a[i] = read(), b[i] = read();
	data();
	for(int i = 2; i <= n; ++i)g[fa[i] = read()].push_back(i);
	dep[1] = 1; dfs(1); dfs2(1, 1);
	for(int i = 1; i <= q; ++i)pre(i);
	for(int i = 1; i <= n; ++i)rem[i] = i;
	S.init(n + n); solve(1);
	// for(int i = 1; i <= q; ++i)printf("%d ",ans[i]); printf("\n");
	for(int i = 1; i <= q; ++i)ans[0] ^= ans[i];
	printf("%d\n",ans[0]);
	return 0;
}

B. 尼特 (nit)

image
image
image

65pts
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int Mxdt = 100000;
inline char gc(){
	static char buf[Mxdt], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, Mxdt, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read(){
	int s = 0, f = 0; char ch = gc();
	while (ch < '0' || ch > '9')f |= (ch == '-'), ch = gc();
	while (ch >= '0' && ch <= '9')s = (s << 3) + (s << 1) + (ch ^ 48), ch = gc();
	return f ? -s : s;
}
const int maxn = 1e6 + 66, 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;
}
int n, m, ans;
int a[maxn];
int f[5005][5005], g[5005][5005];
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int main(){
	freopen("nit.in","r",stdin);
	freopen("nit.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	f[1][0] = 1;
	for(int i = 1; i <= n; ++i){
		for(int j = 0; j <= i; ++j)
			if(a[i] == a[i + 1]){
				add(f[i + 1][j], 1ll * (m - 1) * f[i][j] % mod);
				add(g[i + 1][j], 1ll * (m - 1) * g[i][j] % mod);
				add(f[i + 1][j], f[i][j]);
				add(g[i + 1][j], (f[i][j] + g[i][j]) % mod);
			}else{
				add(f[i + 1][j], 1ll * f[i][j] * (m - 2) % mod);
				add(g[i + 1][j], 1ll * g[i][j] * (m - 2) % mod);
				add(f[i + 1][j + 1], f[i][j]);
				add(g[i + 1][j + 1], (f[i][j] + g[i][j]) % mod);
				if(j){
					add(f[i + 1][j - 1], f[i][j]);
					add(g[i + 1][j - 1], g[i][j]);
				}else{
					add(f[i + 1][j], f[i][j]);
					add(g[i + 1][j], (g[i][j] + f[i][j]) % mod);
				}
			}
	}
	for(int i = 0; i <= n; ++i)add(ans, g[n][i]);
	ans = 1ll * ans * qpow(qpow(m, n - 1), mod - 2) % mod;
	printf("%d\n",ans);
	return 0;
}

C. 苯为 (ber)

发现只要求出环的方案数剩下每个点方案数都是 k1

fii 元环染色方案

先拆成链

fi,0/1 表示 i 元链,最后一个元素与第一个元素颜色相同、不同的方案数

容易写出递推形式,代换一下发现 0/1 可以去掉,只算环的方案

fi=(k1)fi2+(k2)fi1

然后用生成函数可以解出来

fn=(k1)n+(1)n(k1)

求的是

st[(k1)d(A+1)+(1)d(A+1)(k1)](k1)(nd)(A+1)

=st(k1)n(A+1)+(1)d(A+1)(k1)(k1)(nd)(A+1)

=n2(k1)n(A+1)+(k1)st(1)d(A+1)(k1)(nd)(A+1)

后面的部分 dp 求解,相当于选择一条链,每个点贡献为 (1)A+1, 其他点贡献是 (k1)A+1

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

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int Mxdt = 100000;
inline char gc(){
	static char buf[Mxdt], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, Mxdt, stdin), p1 == p2) ? EOF : *p1++;
}
inline ll read(){
	ll s = 0; bool f = 0; char ch = gc();
	while (ch < '0' || ch > '9')f |= (ch == '-'), ch = gc();
	while (ch >= '0' && ch <= '9')s = (s << 3) + (s << 1) + (ch ^ 48), ch = gc();
	return f ? -s : s;
}
const int maxn = 1e6 + 55, mod = 421969921;
int qpow(int x, int y){
	ll ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int n, a, k;
vector<int>g[maxn];
int v1[maxn], v2[maxn], si[maxn], f[maxn], ans;
void solve(int x, int fa){
	si[x] = 1; int res = 0;
	for(int v : g[x])if(v != fa){
		solve(v, x); 
		res = (1ll * res * v2[si[v]] % mod + 1ll * f[x] * f[v] % mod) % mod;
		f[x] = (1ll * f[x] * v2[si[v]] % mod + 1ll * f[v] * v1[1] % mod * v2[si[x] - 1] % mod) % mod;
		si[x] += si[v];
	}	
	ans = (ans + 1ll * v1[1] * v2[n - 1]) % mod;
	ans = (ans + 2ll * res * v2[n - si[x]]) % mod;
	ans = (ans + 2ll * f[x] * v2[n - si[x]]) % mod;
	f[x] = (f[x] + 1ll * v1[1] * v2[si[x] - 1]) % mod;
}
int main(){
	freopen("ber.in","r",stdin);
	freopen("ber.out","w",stdout);
	n = read(); a = read() % (mod - 1); k = read() % mod;
	for(int i = 1; i < n; ++i){
		int x = read(), y = read();
		g[x].push_back(y); g[y].push_back(x);
	}
	v1[0] = v2[0] = 1; 
	v1[1] = ((a + 1) & 1) ? mod - 1 : 1; 
	v2[1] = qpow(k - 1, a + 1);
	for(int i = 2; i <= n; ++i)v1[i] = 1ll * v1[i - 1] * v1[1] % mod, v2[i] = 1ll * v2[i - 1] * v2[1] % mod;
	solve(1, 0);
	ans = 1ll * ans * (k - 1) % mod; 
	ans = (ans + 1ll * n * n % mod * qpow(k - 1, 1ll * n * (a + 1) % (mod - 1))) % mod;
	printf("%d\n",ans);
	return 0;
}

2023 省选联测32

A. 光明 (light)

暴力直接维护子树内每个深度的结点数量,放进桶里

正解就是转成差分的形式

即每次变化时,直接贡献当前点到根节点路径上所有点,那么只需要在数量变化时修改

而用启发式合并容易做到 nlogn 长链剖分做到O(n)

还有一种做法是二分答案 nlogn

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define getc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
char buf[1<<21],*p1,*p2,ch;
ll read(){
	ll ret=0;char c=getc();
	while(c<'0'||c>'9')c=getc();
	while(c>='0'&&c<='9')ret=ret*10+c-'0',c=getc();
	return ret;
}
const int maxn = 3e6 + 55;
ll n, k, cnt[maxn];
int fa[maxn], dep[maxn], mxd[maxn], son[maxn];
vector<int>g[maxn], f[maxn];
int main(){
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	n = read(), k = read(); dep[1] = 1;
	for(int i = 2; i <= n; ++i)g[fa[i] = read()].push_back(i), dep[i] = dep[fa[i]] + 1;
	for(int i = n; i >= 1; --i){
		if(!mxd[i])mxd[i] = dep[i];
		if(mxd[fa[i]] < mxd[i])mxd[fa[i]] = mxd[i], son[fa[i]] = i;
	}
	for(int x = n; x >= 1; --x){
		if(son[x])swap(f[x], f[son[x]]);
		int en = f[x].size();
		for(int v : g[x])if(v != son[x]){
			int s = f[v].size();
			for(int i = 1; i <= s; ++i){
				cnt[f[v].back()] -= dep[x];
				cnt[f[x][en - i]] -= dep[x];
				f[x][en - i] += f[v].back();
				cnt[f[x][en - i]] += dep[x];
				f[v].pop_back();
			}
		}
		f[x].push_back(1);  cnt[1] += dep[x];
	}
	ll ans = 0;
	for(int i = n; i >= 1; --i)
		if(cnt[i] < k){
			ans += cnt[i] * i;
			k -= cnt[i];
		}else{
			ans += i * k;
			break;
		}
	
	printf("%lld\n",ans);
	return 0;
}

B. 游戏 (game)

不难发现是问多少序列存在 >=2k1 的同色连续段

考虑容斥,用总方案减去不存在的

枚举结尾极长连续段长度,可以得到一个 DP

分是否与之前连续段颜色相同两种转移

fi>fi+1

fi(m1)>f1

容易写成矩阵乘法,有了一个 k3logn优秀做法

可以用什么特征多项式得到递推式,好像能砍掉个k ?

简单思考可以把他变成前缀和优化的形式,也可以 O(n) 求解

我太菜了不会

现在能处理 k 比较小的情况,考虑对 k 比较大的用另外的方法求解

题解上面的 DP 与我的有所区别,所以是另外一种 DP 来思考的。。

下面大量引用题解

设这里的 k 是原题的 2k1,我们反过来求不存在长度为 k 的连续段。

考虑假设已经给序列分好段,每段都是极长的数字相同的段,设有 x 段那么对答案的贡献就是 m(m1)x1=mm1(m1)x 表示每一段都和前一段数字不一样。

按这个方式 dp,设 fi 表示划分 [1,i],枚举当前段长度 j 就得到

f0=1,fi=(m1)j=1min(k1,i)fij,ans=mm1fn

用生成函数进行刻画

mm1[xn]t>=0((m1)i=1k1xi)t

t 是枚举不同的颜色连续段个数)

=mm1[xn]11(m1)xkxx1

=mm1[xn]x1mx1(m1)xk

乘上 x1 相当于差分

所以我们只需要做到能求

[xn]1mx1(m1)xk=[xn]11(mx(m1)xk)

即可(上式需要计算 nn1),就是

f(n)=[xn]t>=0(mx+(1m)xk)t

=t>=0i>=0(ti)(1m)imti[ik+ti==n]

=i>=0(ni(k1)i)(1m)imnik

使用 lucas 即可求解

这部分是 O(p+nklogpn)

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;
}
int n, m, k, mod;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * x * ans % mod;
	return ans;
}
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
struct matrix{
	ll a[505][505];
	matrix(){memset(a, 0, sizeof(a));}
	friend matrix operator *(const matrix &x, const matrix &y){
		matrix res; ll tmp;
		for(int i = 0; i < k; ++i)
			for(int t = 0; t < k; ++t){
				tmp = x.a[i][t];
				if(tmp)
				for(int j = 0; j < k; ++j)
					res.a[i][j] += tmp * y.a[t][j];
			}
		for(int i = 0; i < k; ++i)
			for(int j = 0; j < k; ++j)
				res.a[i][j] %= mod;
		return res;
	}
};
void matrix_(){
	static matrix tr, res;
	res.a[0][0] = m;
	for(int i = 0; i < k; ++i)tr.a[i][0] = m - 1, tr.a[i][i + 1] = 1; 
	if(k)tr.a[k - 1][k] = 0;
	int y = n - 1;
	for(; y; y >>= 1, tr = tr * tr)if(y & 1)res = res * tr;
	int ans = 0;
	for(int i = 0; i < k; ++i)add(ans, res.a[0][i]);
	ans = (qpow(m, n) - ans + mod) % mod;
	printf("%d\n",ans);
}
const int maxn = 1e7 +55;
int fac[maxn], ifac[maxn];
int c(int n, int m){return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int lucas(int n, int m){
	if(n < m)return 0;
	if(m == 0)return 1;
	if(n < mod && m < mod)return c(n, m);
	return 1ll * lucas(n % mod, m % mod) * lucas(n / mod, m / mod) % mod;
}
int pm[maxn];
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = read(), m = read(), k = read(), mod = read(); k = k + k - 2;
	if(n <= k){printf("0\n");return 0;}
	if(k <= 500){matrix_();return 0;}
	++k; 
	fac[0] = ifac[0] = 1;
	for(int i = 1; i < mod; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[mod - 1] = qpow(fac[mod - 1], mod - 2);
	for(int i = mod - 2; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	pm[0] = 1; for(int i = 1; i < mod; ++i)pm[i] = 1ll * pm[i - 1] * m % mod;
	int res = 0, mk = 1;
	for(int i = 0; 1ll * i * k <= n; ++i){
		res = (res + 1ll * lucas(n - i * (k - 1), i) * mk % mod * pm[(n - i * k) % (mod - 1)]) % mod;
		mk = 1ll * mk * (1 - m + mod) % mod;
	}
	mk = 1;
	for(int i = 0; 1ll * i * k <= n - 1; ++i){
		res = (res - 1ll * lucas(n - 1 - i * (k - 1), i) * mk % mod * pm[(n - 1 - i * k) % (mod - 1)]) % mod;
		mk = 1ll * mk * (1 - m + mod) % mod;
	}
	res = (1ll * res * m % mod * qpow(m - 1, mod - 2) % mod + mod) % mod;
	res = (qpow(m, n) - res + mod) % mod;
	printf("%d\n",res);
	return 0;
}

C. 皇后 (queen)

题解是什么调整法

实际上随机化乱搞就能搞到不少分

在每一层 shuffle 决策的列,然后去掉选过的,多跑几次好像就能切

然后有个这个东西

https://www.cnblogs.com/RioTian/p/14745949.html

所以不是说n皇后 是 NP 问题吗。。。。

2023 省选联测33

A. 张三

双指针

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 = 1e6 + 55;
char c[maxn];
int n, k, sum[maxn];
int main(){
	freopen("kinetickudu.in","r",stdin);
	freopen("kinetickudu.out","w",stdout);
	scanf("%s", c + 1);
	n = strlen(c + 1);
	scanf("%d",&k);
	for(int i = 1; i <= n; ++i)sum[i] = sum[i - 1] + (c[i] ^ 48);
	int l = 1, ans = 0;
	for(int r = 1; r <= n; ++r){
		while(sum[r] - sum[l - 1] > k)++l;
		ans = max(ans, r - l + 1);
	}
	printf("%d\n",ans);
	return 0;
}

B. 李四

直接状压,注意用状压的方式进行bfs 保证复杂度

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 = 25;
int n, m, k, land, cnt[(1 << 23) + 55], lg[(1 << 23) + 55];
vector<int>st[maxn];
int g[maxn];
uint f[(1 << 23) + 55];
void trans(int s){
	int now = (s & land) ^ land;
	if(!now)return;
	int las = 0;
	while(now ^ las){
		int ne = now ^ las; las = now;
		for(int i = ne; i; i -= (i & -i)) now |= g[lg[i & -i]];
		now = now ^ (now & s);
	}
	for(int i = now; i; i -= i & -i)f[s | (i & -i)] += f[s];
}
int main(){
	freopen("jammyjellyfish.in","r",stdin);
	freopen("jammyjellyfish.out","w",stdout);
	n = read(), m = read(), k = read();
	for(int i = 1; i <= k; ++i)land |= (1 << (read() - 1));
	for(int i = 1, u, v; i <= m; ++i){
		u = read(), v = read();
		g[u - 1] |= (1 << (v - 1));
		g[v - 1] |= (1 << (u - 1));
	}
	int mx = (1 << n);
	for(int i = 1; i < mx; ++i){
		cnt[i] = cnt[i >> 1] + (i & 1);
		st[cnt[i]].push_back(i);
	}
	for(int i = 2; i < mx; i <<= 1)lg[i] = lg[i >> 1] + 1;
	st[0].push_back(0); f[0] = 1;
	for(int i = 0; i < n; ++i){
		for(int s : st[i])trans(s);
	}
	printf("%u\n",f[mx - 1]);
	return 0;
}

C. 王五

最多存在 m 种长度,直接 hash 处理

也可以 AC 自动机上启发式

code
// ubsan: undefined
// accoders
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ull, int> pui;
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;
}
int id(char c){
	if(c == 'A')return 1;
	if(c == 'T')return 2;
	if(c == 'C')return 3;
	if(c == 'G')return 4;
}
const int maxn = 1e5 + 55, mod = 1e9 + 7, base2 = 5;
// const ull base1 = 7;
void ckmx(int &x, int y){if(x < y)x = y;}
string s[maxn];
int n, len[maxn], m, b, mx;
vector<int>rem[maxn], h2[maxn];
// vector<ull>h1[maxn];
// ull p1[maxn];
int p2[maxn];
// map<pui, int>mp, tmp;
unordered_map<int, int>mp, tmp;
int cont;
// pui get_hash(int i, int l, int r){
// 	++cont;
// 	return pui(h1[i][r] - (h1[i][l - 1] * p1[r - l + 1]),(h2[i][r] - (1ll * h2[i][l - 1] * p2[r - l + 1] % mod) + mod) % mod);	
// }
int get_hash(int i, int l, int r){
	// ++cont;
	return //pui(
		// h1[i][r] - (h1[i][l - 1] * p1[r - l + 1]);
		// ,
		(h2[i][r] - (1ll * h2[i][l - 1] * p2[r - l + 1] % mod) + mod) % mod;	
}
int ans[maxn];
void sol(int l){
	for(int p = l; p <= mx; ++p)
		for(int i : rem[p]){
			tmp.clear();
			for(int r = l; r <= len[i]; ++r)++tmp[get_hash(i, r - l + 1, r)];
			for(auto it : tmp)ckmx(mp[it.first],it.second);
		}
	for(int v : rem[l])ans[v] = mp[get_hash(v, 1, l)];
	mp.clear();
}
int main(){
	freopen("impishindri.in","r",stdin);
	freopen("impishindri.out","w",stdout);
	cin >> n;
	for(int i = 1; i <= n; ++i)cin >> s[i];
	for(int i = 1; i <= n; ++i){
		m += len[i];
		len[i] = s[i].size();
		rem[len[i]].push_back(i);
		mx = max(len[i], mx);
	}
	// p1[0] = 1; 
	p2[0] = 1; 
	// for(int i = 1; i <= mx; ++i)p1[i] = p1[i - 1] * base1;
	for(int i = 1; i <= mx; ++i)p2[i] = 1ll * p2[i - 1] * base2 % mod;
	for(int i = 1; i <= n; ++i){
		// h1[i].push_back(0);
		 h2[i].push_back(0);
		// ull tmp1 = 0; 
		int tmp2 = 0;
		for(int j = 1; j <= len[i]; ++j){
			// tmp1 = tmp1 * base1 + id(s[i][j - 1]);
			tmp2 = (1ll * tmp2 * base2 + id(s[i][j - 1])) % mod;
			// h1[i].push_back(tmp1);
			h2[i].push_back(tmp2);
		}
	}
	for(int i = 1; i <= mx; ++i)if(rem[i].size())sol(i);	
	for(int i = 1; i <= n; ++i)printf("%d\n",ans[i]);
	return 0;
}

D. 赵六

最短路树 + 支配树板子

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pli;
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;
int head[maxn], tot;
struct edge{int to, net, val;}e[maxn << 1 | 1];
void add(int u, int v, int w){
	e[++tot] = {v, head[u], w};
	head[u] = tot;
}
int n, m, cnt;
ll dis[maxn]; bool vis[maxn];
priority_queue<pli, vector<pli>, greater<pli>>q;
vector<int>g[maxn];
int dep[maxn], fa[maxn][19], ans[maxn];
int LCA(int u, int v){
	if(dep[u] < dep[v])swap(u, v);
	for(int i = 18; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))u = fa[u][i];
	if(u == v)return u;
	for(int i = 18; i >= 0; --i)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}
int main(){
	freopen("hirsutehippo.in","r",stdin);
	freopen("hirsutehippo.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read(), w = read(); 
		add(u, v, w); add(v, u, w);
	}
	q.push(pli(0, 1)); memset(dis, 0x3f, sizeof(dis)); dis[1] = 0;
	while(!q.empty()){
		int x = q.top().second; q.pop();
		if(vis[x])continue; vis[x] = true;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(dis[v] > dis[x] + e[i].val){
				dis[v] = dis[x] + e[i].val;
				q.push(pli(dis[v], v));
			}
		}
	}
	for (int x = 1; x <= n; ++x)
		for (int i = head[x]; i; i = e[i].net) {
			int v = e[i].to;
			if (dis[v] == dis[x] + e[i].val){
				g[v].push_back(x);
			}
		}
	for(int i = 2; i <= n; ++i)q.push(pli(dis[i], i));
	while(!q.empty()){
		int x = q.top().second; q.pop();
		if(g[x].size() == 1){
			fa[x][0] = g[x].back();
			ans[x] = ans[fa[x][0]] + 1;
		}else{
			int tmp = g[x].back();
			for(g[x].pop_back(); g[x].size(); g[x].pop_back())tmp = LCA(tmp, g[x].back());
			fa[x][0] = tmp;
			ans[x] = ans[tmp];
		}
		dep[x] = dep[fa[x][0]] + 1;
		for(int i = 1; i <= 18; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
	}
	for(int i = 1; i <= n; ++i)printf("%d\n",ans[i]);	
	return 0;
}

2023 省选联测34

没素质的模拟,难度也不太对劲啊

太菜了不想写题解,看 chino 大佬的吧

A. 调度

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; bool f = false; char c = getchar();
	while(!isdigit(c))f = c == '-', c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}
const int maxn = 1e6 + 55;
int n, k, a[maxn];
bool solve(){
	n = read(); k = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)a[i] -= read();
	for(int i = n; i >= 2; --i)a[i] -= a[i - 1];
	for(int i = 1; i <= n; ++i)if(a[i]){
		if(i + k > n + 1)return false;
		a[i + k] += a[i];
	}
	return true;
}
int main(){
	freopen("dispatch.in","r",stdin);
	freopen("dispatch.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)
		if(solve())printf("YES\n");
		else printf("N0\n");
	return 0;
}

B. 大订单

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 = 1505;
void ckmx(int &x, int y){if(x < y)x = y;}
bool vis[maxn];
int tr[maxn][6][6], pos[35], zs[35];
#define val(x, i) ((x >> pos[i]) & 3)
queue<int>q;
void pre(){
	for(int i = 0; i <= 10; ++i)pos[i] = i << 1, zs[i] = 1 << pos[i];
	memset(tr, -1, sizeof(tr));
	q.push(0); vis[0] = true;
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int i = 1; i <= 4; ++i)
			if(val(x, i) == 0 && (!vis[x + zs[i]]))
				vis[x + zs[i]] = true, q.push(x + zs[i]);
		for(int l = 0; l <= 5; ++l)
			for(int r = l + 1; r <= 5; ++r){
				bool flag = (val(x, l) <= 1);
				for(int k = l + 1; k < r; ++k)flag &= !val(x, k);
				if(!flag)continue;
				int to = x + (2 - val(x, l)) * zs[l];
				for(int k = l + 1; k < r; ++k)to += 3 * zs[k];
				tr[x][l][r] = to;
				if(!vis[to])vis[to] = true, q.push(to);
			}
	}
}
int n, k, a[maxn], f[2][maxn][105][6][2];
vector<int>v;
int main(){	
	freopen("order.in","r",stdin);
	freopen("order.out","w",stdout);
	pre(); n = read(), k = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	memset(f, 0xc0, sizeof(f));
	int now = 0;
	for(int i = 1; i < 5; ++i)f[now][zs[i]][1][i][0] = 0;
	f[now][0][1][0][0] = f[now][0][1][5][0] = 0;
	for(int i = 0; i <= 1024; ++i)if(vis[i])v.push_back(i);
	for(int i = 1; i <= n; ++i){
		int to = now ^ 1;
		for(int s : v)memset(f[to][s], 0xc0, sizeof(f[to][s]));
		for(int s : v)
			for(int j = 1; j <= min(i, k); ++j)
				for(int l = 0; l <= 5; ++l)
					for(int p = 0; p <= 1; ++p)if(f[now][s][j][l][p] >= 0){
						ckmx(f[to][s][j][l][p], f[now][s][j][l][p] + (a[i] == l));
						if(val(s, a[i]) != 3){
							if(a[i] > 0 && a[i] < 5 && !val(s, a[i]))
								ckmx(f[to][s | zs[a[i]]][j + 1][a[i]][0], f[now][s][j][l][p] + 1);
							else 
								ckmx(f[to][s][j + 1][a[i]][0], f[now][s][j][l][p] + 1);
						}
						if(tr[s][l][a[i]] != -1 && !(p && val(s, l)))ckmx(f[to][tr[s][l][a[i]] + p * zs[l]][j][a[i]][1], f[now][s][j][l][p] + 1);
					}
		now ^= 1;
	}
	for(int i = 1; i <= k; ++i){
		int ans = 0;
		for(int s : v)
			for(int j = 0; j <= 5; ++j)
				for(int p = 0; p <= 1; ++p)
					ckmx(ans, f[now][s][i][j][p]);
		printf("%d ",ans);
	}
	return 0;
}

C. 抵达顶端

code

D. 地对地导弹

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 = 4005, mod = 998244853;
int id(char c){
	if(c == 'D')return 1;
	if(c == 'Q')return 2;
	if(c == 'P')return 3;
	assert(0); 
}
int n, k, ans, a[maxn], f[maxn][maxn + maxn][4], p3[5];
char s[maxn];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int main(){
	freopen("df.in","r",stdin);
	freopen("df.out","w",stdout);
	n = read(), k = read(); scanf("%s",s + 1);
	for(int i = 2; i < n; ++i)if(s[i - 1] == 'D' && s[i] == 'Q' && s[i + 1] == 'P'){
		a[i - 1] = a[i] = a[i + 1] = 3;
	}
	p3[0] = 1; for(int i = 1; i <= 3; ++i)p3[i] = p3[i - 1] * 3;
	s[0] = s[n + 1] = '?';
	for(int i = 1, len = 0; i <= n; ++i){
		if(a[i] == 3)++len;
		else{
			if(s[i] == 'Q' && s[i + 1] == 'P' && len){
				a[i] = a[i + 1] = -2; len += 2; ++i;
			}else if(s[i] == 'P' && len >= 2){
				a[i] = -1; ++len;
			}else len = 0;
		}
	}
	for(int i = n, len = 0; i >= 1; --i){
		if(a[i] == 3)++len;
		else{
			if(s[i] == 'Q' && len && a[i - 1]){
				a[i] = -3; ++len; 
			}else if(s[i] == 'Q' && s[i - 1] == 'D' && len){
				a[i] = a[i - 1] = 2; len += 2; --i;
			}
			else if(s[i] == 'D' && len >= 2){
				a[i] = 1; ++len;
			}else len = 0;
		}
	}
	// for(int i = 1; i <= n; ++i)printf("%d ",a[i]); printf("\n");
	f[1][0][0] = 1;
	for(int i = 1, len; i <= n; i += len){
		len = a[i] == -3 ? 1 : (a[i] == 0 ? 1 : abs(a[i]));
		for(int j = 0; j <= k; ++j){
			add(f[i + len][j][0], f[i][j][0]);
			if(f[i][j][0] && a[i] > 0){
				// printf("%d %d\n", i, len);
				if(a[i] == 3)add(f[i + len][j + 1][1], f[i][j][0]), add(f[i + len][j + 1][2], 26ll * f[i][j][0] % mod);
				else add(f[i + len][j + 1][3], 1ll * (p3[len] - 1) * f[i][j][0] % mod);
			}
			if(f[i][j][1] && a[i] < 0){
				if(a[i] == -3)add(f[i + len][j + 1][3], 2ll * f[i][j][1] % mod);
				else add(f[i + len][j + 1][1], f[i][j][1]), add(f[i + len][j + 1][2], 1ll * (p3[len] - 1) * f[i][j][1] % mod);
			}
			if(f[i][j][2]){
				add(f[i + len][j][0], f[i][j][2]);
				int tmp = 1ll * (p3[len] - 1) * f[i][j][2] % mod;
				if(a[i] == 3)add(f[i + len][j + 1][1], f[i][j][2]), add(f[i + len][j + 1][2], tmp);
				else if(a[i] > 0 || a[i] == -3)add(f[i + len][j + 1][3], tmp);
				else if(a[i])add(f[i + len][j + 1][1], f[i][j][2]), add(f[i + len][j + 1][2], tmp);
			}
			if(f[i][j][3] && a[i] > 0){
				add(f[i + len][j + 1][a[i] == 3 ? 2 : 3], 1ll * p3[len] * f[i][j][3] % mod);
			}
			// printf("i = %d j = %d [0] = %d [1] = %d [2] = %d [3] = %d\n",i, j, f[i][j][0], f[i][j][1], f[i][j][2], f[i][j][3]);
		}
	}
	int ans = 0;
	for(int i = 0; i <= k; ++i)add(ans, f[n + 1][i][0]), add(ans, f[n + 1][i][2]);
	printf("%d\n",ans);
	return 0;
}

2023 省选联测35

A. 遗忘十字路

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;
int n, k, s[maxn], dep[maxn], fa[maxn], res[maxn];
vector<int>g[maxn];
ll ans;
multiset<ll>f[maxn];
void merge(multiset<ll>&x, multiset<ll>&y){
	if(x.size() < y.size())swap(x, y);
	for(ll v : y)x.insert(v); y.clear();
}
void dfs(int x){
	int deg = g[x].size() - (x != 1);
	ans += 1ll * res[x] * s[x];
	if(!deg){f[x].insert(s[x]);return;}
	int to = res[x] / deg; res[x] %= deg;
	for(int v : g[x])if(v != fa[x]){
		dep[v] = dep[x] + 1; fa[v] = x;
		res[v] = to; dfs(v);
		merge(f[x], f[v]);
	}
	for(int i = 1; i <= res[x]; ++i){
		ans += *--f[x].end();
		f[x].erase(--f[x].end());
	}
	while(f[x].size() > 1)f[x].erase(f[x].begin());
	ll v = *f[x].begin(); f[x].clear(); f[x].insert(v + s[x]);
}
void sol(){
	n = read(), k = read();
	for(int i = 1; i <= n; ++i)s[i] = read();
	for(int i = 1, u, v; i < n; ++i){
		u = read(), v = read(); 
		g[u].push_back(v);
		g[v].push_back(u);
	}
	ans = 0; res[1] = k; dfs(1);
	printf("%lld\n",ans);
	for(int i = 1; i <= n; ++i){
		g[i].clear(); f[i].clear();
	}
}
int main(){
	freopen("crossroad.in","r",stdin);
	freopen("crossroad.out","w",stdout);
	int t = read(); for(int i = 1; i <= t; ++i)sol();
	return 0;
}

B. 鹿角虫道

code
// ubsan: undefined
// accoders
#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, maxn = 13, inv2 = (mod + 1) >> 1;
int n, m, e[maxn][maxn], f[maxn][1 << maxn], g[maxn][1 << maxn], h[maxn][maxn][1 << maxn];
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int main(){
	// freopen("stag.in","r",stdin);
	// freopen("stag.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; ++i){
		int u = read() - 1, v = read() - 1;
		e[u][v] = 1; e[v][u] = 1;
	}
	for(int i = 0; i < n; ++i)f[i][0] = 1;
	for(int s = 1; s < (1 << n); ++s){
		for(int i = 0; i < n; ++i)if(s >> i & 1){
			for(int j = 0; j < n; ++j)if(i != j && ((s >> j) & 1)){
				for(int k = 0; k < n; ++k)if(i != k && j != k && ((s >> k) & 1) && e[i][k]){
					int tmp = s ^ (1 << j) ^ (1 << k);
					for(int t = tmp; t; t = (t - 1) & tmp)if((t >> i) & 1)
						add(h[i][j][s], 1ll * h[k][j][s - t] * f[i][t ^ (1 << i)] % mod);
				}
				if(e[i][j]){
					int tmp = s ^ (1 << j);
					for(int t = tmp; t; t = (t - 1) & tmp)if((t >> i) & 1)
						add(h[i][j][s], 1ll * f[i][t ^ (1 << i)] * f[j][(s - t) ^ (1 << j)] % mod);
				}
			}
		}
		int low = __lg(s & -s);
		for(int i = 0; i < n; ++i)if(!((s >> i) & 1)){
			for(int t = s; t; t = (t - 1) & s)if((t >> low) & 1){
				add(f[i][s], 1ll * g[i][t] * f[i][s ^ t] % mod);
				for(int k = 0; k < n; ++k)if(((t >> k) & 1) && e[i][k])
					add(f[i][s], 1ll * f[low][t ^ (1 << low)] * f[i][s ^ t] % mod);
			}
			for(int j = 0; j < n; ++j)if(i != j && ((s >> j) & 1) && e[i][j]){
				for(int k = 0; k < n; ++k)if(i != k && j != k && ((s >> k) & 1) && e[i][k]){
					add(f[i][s], 1ll * h[k][j][s] * inv2 % mod);
					add(g[i][s], 1ll * h[k][j][s] * inv2 % mod);
				}
			}
		}
	}
	printf("%d\n",f[0][(1 << n) - 2]);
	return 0;
}

C. 泪水之城

code

D. 生命血

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 = 4e5 + 55;
int n, m, p[maxn]; ll a[maxn];
struct node{int l, r, v; void in(){l = read(), r = read(), v = read();}}d[maxn];
bool cmp(const int &x, const int &y){return d[x].v > d[y].v;}
vector<int>ans; ll sum;
struct seg{
	struct node{
		ll val, tag;
	}t[maxn << 2 | 1];
	bool flag;
	void push_up(int x){
		if(flag)t[x].val = max(t[x << 1].val, t[x << 1 | 1].val); 
		else t[x].val = min(t[x << 1].val, t[x << 1 | 1].val);
	}
	void build(int x, int l, int r){
		t[x].tag = 0;
		if(l == r){
			t[x].val = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	void upd(int x, int val){t[x].val += val; t[x].tag += val;}
	void push_down(int x){if(t[x].tag)upd(x << 1, t[x].tag), upd(x << 1 | 1, t[x].tag), t[x].tag = 0;}
	void modify(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R)return upd(x, val);
		push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid) modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	ll query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].val;
		push_down(x); int mid = (l + r) >> 1;
		ll ans = flag ? -LONG_LONG_MAX : LONG_LONG_MAX;
		ll lans = L <= mid ? query(x << 1, l, mid, L, R) : ans;
		ll rans = R > mid ? query(x << 1 | 1, mid + 1, r, L, R) : ans;
		return flag ? max(lans, rans) : min(lans, rans);
	}
}L, R;
void print(){
	printf("%lld\n",sum);
	printf("%d\n",(int)ans.size());
	if(ans.size()){
		int en = ans.back(); ans.pop_back();
		for(int v : ans)printf("%d ",v);
		printf("%d\n",en);
	}
	ans.clear(); sum = 0;
}

void solve(){
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i] = read() + a[i - 1];
	L.flag = 1; L.build(1, 0, n); R.build(1, 1, n); 
	for(int i = 1; i <= m; ++i)d[i].in(), p[i] = i;
	sort(p + 1, p + m + 1, cmp);
	for(int i = 1; i <= m; ++i){
		int now = p[i];
		if(L.query(1, 0, n, 0, d[now].l - 1) < R.query(1, 1, n, d[now].r, n)){
			ans.push_back(now); sum += d[now].v;
			L.modify(1, 0, n, d[now].l, n, -1);
			R.modify(1, 1, n, d[now].r, n, -1);
		}
	}
	print();
}

int main(){
	freopen("lifeblood.in","r",stdin);
	freopen("lifeblood.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

2023 省选联测36

A. 太空漫步

不要写刷表法,难写到吐

(也可能是我忽略了不存在相邻特殊字符的限制)

直接暴力填表

code
// ubsan: undefined
// accoders
#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 = 1e6 + 55;
char s[maxn], c[maxn]; 
int n, m, rem[maxn], st[maxn], top;
bool f[maxn][105];
void solve(){
	scanf("%s", c + 1); m = strlen(c + 1);
	for(int i = 0; i <= m; ++i)
		for(int j = 0; j <= n; ++j)
			f[i][j] = 0;
	f[0][0] = 1;
	for(int i = 0; i <= m; ++i){
		for(int j = 0; j <= n; ++j){
			if(s[j] == '?'){
				if(i && j)f[i][j] |= f[i - 1][j - 1];
			}else if(s[j] == '*'){
				if(i)f[i][j] |= f[i - 1][j];
				if(j)f[i][j] |= f[i][j - 1];
			}else if(s[j] == '['){
				if(j)f[i][j] |= f[i][j - 1];
			}else if(s[j] == ']'){
				if(j)f[i][j] |= f[i][j - 1];
				f[i][j] |= f[i][rem[j]];
			}else{
				if(i && j)f[i][j] |= (f[i - 1][j - 1] && (s[j] == c[i]));
			} 
		}
		if(i)printf("%d",f[i][n]);
	}
	printf("\n");
}
int main(){
	freopen("walking.in","r",stdin);
	freopen("walking.out","w",stdout);
	scanf("%s",s + 1); n = strlen(s + 1); s[0] = s[n + 1] = '#';
	for(int i = 1; i <= n; ++i)if(s[i] == '[')st[++top] = i;
	else if(s[i] == ']')rem[i] = st[top], rem[st[top]] = i, --top;
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

B. 树的解构

一眼题

分开算每个点的贡献,发现只与深度有关

枚举第一次断掉哪条边变成子任务

发现是个前缀和的形式

但是 NMD 为啥不保证 fai<i。。。。。。

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 = 2e6 + 66, mod = 1e9 + 7;
int n, fa[maxn], dep[maxn], f[maxn], inv[maxn];
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){e[++tot] = {v, head[u]}; head[u] = tot;}
void dfs(int x){
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to; dep[v] = dep[x] + 1;
		dfs(v);
	}
}
int main(){
	freopen("deconstruct.in","r",stdin);
	freopen("deconstruct.out","w",stdout);
	n = read(); 
	for(int i = 2; i <= n; ++i)add(fa[i] = read(), i);
	dfs(1);
	inv[1] = 1;
	for(int i = 2; i <= n; ++i)inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
	for(int i = 1, sum = 0; i <= n; ++i)
		f[i] = (1ll * inv[i] * sum % mod + 1) % mod, sum = (sum + f[i]) % mod;	
	int ans = 0;
	for(int i = 1; i <= n; ++i)ans = (ans + f[dep[i]]) % mod;
	printf("%d\n",ans);
	return 0;
}

C. 小 T 与灵石

不难发现每次询问的点集只用找到直径

后面就比较神奇了

考虑每个点到直径端点的最远距离一定是到半径的距离加上直径的一半

于是建一个虚点,向直径中点连直径长度一半的边,答案就是最短路

由于在树上,于是可以两遍 dfs

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 = 3e5 + 55;
int n, fa[maxn], son[maxn], si[maxn], dep[maxn], top[maxn], dfn[maxn], tim, id[maxn];
int dis[maxn];
int down[maxn], up[maxn];
vector<int>g[maxn];
void dfs1(int x){
	si[x] = 1;
	for(int v : g[x]){
		dep[v] = dep[x] + 1; 
		dfs1(v); si[x] += si[v];
		if(si[son[x]] < si[v])son[x] = v;
	}
}
void dfs2(int x, int tp){
	top[x] = tp; id[dfn[x] = ++tim] = x;
	if(son[x])dfs2(son[x], tp);
	for(int v : g[x])if(v != son[x])dfs2(v, v);
}
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
int Dis(int u, int v){return dep[u] + dep[v] - 2 * dep[lca(u, v)];}
int kfa(int x, int k){
	if(x == 1)return x;
	while(x){
		int up = min(k, dfn[x] - dfn[top[x]]);
		x = id[dfn[x] - up];
		if(x == 1)return x;
		k -= up;
		if(k == 0)break;
		x = fa[x]; --k;
	}
	return x;
}
int p[maxn];
void solve(){
	int k = read();
	for(int i = 1; i <= k; ++i)p[i] = read();
	int s = p[1], t = p[1];
	for(int i = 1; i <= k; ++i)if(Dis(s, t) < Dis(s, p[i]))t = p[i];
	for(int i = 1; i <= k; ++i)if(Dis(s, t) < Dis(p[i], t))s = p[i];
	int len = Dis(s, t); if(dep[s] < dep[t])swap(s, t);
	int mid = kfa(s, len >> 1);
	down[mid] = min(down[mid], len - dep[s]);
	if(len & 1)up[fa[mid]] = min(up[fa[mid]], dep[s]);
	else up[mid] = min(up[mid], dep[s]);
}
void Up(int x){for(int v : g[x])Up(v), up[x] = min(up[x], up[v]);}
void Down(int x){
	down[x] = min(down[x], up[x] - 2 * dep[x]);
	for(int v : g[x])down[v] = min(down[v], down[x]), Down(v);
}
int main(){
	freopen("stone.in","r",stdin);
	freopen("stone.out","w",stdout);
	n = read();
	for(int i = 2; i <= n; ++i)g[fa[i] = read()].push_back(i);
	dfs1(1); dfs2(1, 1); 
	for(int i = 1; i <= n; ++i)up[i] = down[i] = n + n + n;
	int q = read();
	for(int i = 1; i <= q; ++i)solve();
	Up(1); Down(1);
	for(int i = 1; i <= n; ++i)dis[i] = down[i] + dep[i];
	for(int i = 1; i <= n; ++i)printf("%d\n",dis[i]);
	return 0;
}

D. 小 S 埋地雷

image

不是很理解很是不理解

注意如果u不在当前区间内的话实际上对本区间没有任何限制

还有一堆细节不会

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
mt19937 rd((ull)(new char) * (ull)(new char));
int read(){
	int x = 0; bool f = false; char c = getchar();
	while(!isdigit(c))f = c == '-', c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}
const int maxn = 75;
int n, p[maxn], q[maxn], r[maxn], s[maxn];
void ckmx(int &x, int y){if(x < y)x = y;}
int sq(int x){return x * x;}
int calc(int a, int b, int c, int d){return sq(p[a] - q[b]) + sq(p[b] - r[c]) + sq(p[c] - s[d]);}
int f[maxn][maxn][maxn][maxn];
void solve(){
	for(int len = 1; len <= n; ++len)
		for(int l = 1, r = len; r <= n; ++l, ++r)
			for(int t = r + 1; t <= n + 1; ++t)
				for(int u = l; u <= r; ++u)
					for(int k = u; k <= r; ++k)
						for(int v = k + 1; v <=	r + 1; ++v)
							ckmx(f[l][r][t][u], f[l][k - 1][v][u < k ? u : l] + f[k + 1][r][t][v > r ? k + 1 : v] + calc(l - 1, k, r + 1, t));
	int ans = 0;
	for(int i = 1; i <= n + 1; ++i)ckmx(ans, f[1][n][n + 1][i]);
	printf("%d\n",ans);
}	
int main(){
	freopen("landmine.in","r",stdin);
	freopen("landmine.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)p[i] = read();
	for(int i = 1; i <= n; ++i)q[i] = read();
	for(int i = 1; i <= n; ++i)r[i] = read();
	for(int i = 1; i <= n; ++i)s[i] = read();
	solve();
	return 0;
}

2023 省选联测37

A. 玩水

原题 https://www.cnblogs.com/Chencgy/p/16712647.html

B. 假人

这个东西很像闵可夫斯基和,但是直接搞没有凸性

把长度减一,设 fi,j 表示选了 i 个,长度和为 j 的最大值

发现 fi,j+12k 有凸性,简单来说就是用 [0,4] 之间的数表示出来 24 一定能拆成两个 12

于是就能搞了

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

using namespace std;
typedef long long ll;
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 = 1e6 + 55;
void ckmx(ll &x, ll y){if(x < y)x = y;}
struct poly{
	vector<ll>f;
	int deg(){return f.size();}
	void cf(){for(int i = f.size() - 1; i > 0; --i)f[i] -= f[i - 1];}
	void sum(){for(int i = 1; i < f.size(); ++i)f[i] += f[i - 1];}
	void print(){for(ll v : f)printf("%lld ", v);}
}f[maxn][13], g[13];
int n, top;
ll tmp[maxn * 6];
void merge(const poly &x, const poly &y, poly &res, int fir){
	if(x.f.empty() || y.f.empty())return;
	tmp[top = 0] = x.f[0] + y.f[0];
	int sx = x.f.size(), sy = y.f.size(), px = 1, py = 1;
	while(px < sx && py < sy){
		if(x.f[px] < y.f[py])tmp[++top] = y.f[py++];
		else tmp[++top] = x.f[px++];
	}
	while(px < sx)tmp[++top] = x.f[px++];
	while(py < sy)tmp[++top] = y.f[py++];
	for(int i = 1; i <= top; ++i)tmp[i] += tmp[i - 1];
	while(res.f.size() < top + 1 + fir)res.f.push_back(0); 
	for(int i = fir; i <= top + fir; ++i)res.f[i] = max(res.f[i], tmp[i - fir]);
}
void solve(int l ,int r){
	if(l >= r)return;
	int mid = (l + r) >> 1;
	solve(l, mid);
	solve(mid + 1, r);
	for(int i = 0; i < 12; ++i)g[i].f.clear();
	for(int i = 0; i < 12; ++i)f[l][i].cf(), f[mid + 1][i].cf();
	for(int i = 0; i < 12; ++i)if(f[l][i].f.size())
		for(int j = 0; j < 12; ++j)if(f[mid + 1][j].f.size())
			merge(f[l][i], f[mid + 1][j], g[(i + j) % 12], (i + j) >= 12);
	swap(f[l], g);
}
int main(){
	freopen("fake.in","r",stdin);
	freopen("fake.out","w",stdout);
	n = read(); int sum = 0;
	for(int i = 1; i <= n; ++i){
		int k = read(); sum += k - 1;
		for(int j = 1; j <= k; ++j)f[i][j - 1].f.push_back(read());
	}
	solve(1, n);
	for(int i = 0; i <= sum; ++i)printf("%lld ",f[1][i % 12].f[i / 12]);
	return 0;
}

C. 切题

题解说用霍尔定理,可以得到有解的充要条件

a 的所有排列 p

k[1,n]i=1kpii=1mmin(bi,k)

发现只要 a 的降序排列满足即可

把上面的式子写成

i=1kaif(k)

f(k)i=1kai0

线段树维护这个东西的最小值,即可快速判断

如何维护?

考虑 b 变化时,以 +1 为例

k<=b 的并无影响

k>b 的都 +1

于是需要区间加

a 维护,发现需要维护有序的数列

考虑维护每个数值的位置集合

如果是加操作,那么修改第一个,否则修改最后一个

发现这样不会影响相对大小关系

仍然是区间加操作

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

using namespace std;
typedef long long ll;
#define int long long
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 = 6e5 + 55;
int n, m, a[maxn], b[maxn], c[maxn];
ll val[maxn], sa[maxn];
struct seg{
	struct node{
		ll mi; int tag;
	}t[maxn << 2 | 1];
	void push_up(int x){t[x].mi = min(t[x << 1].mi, t[x << 1 | 1].mi);}
	void upd(int x, int v){t[x].mi += v; t[x].tag += v;}
	void push_down(int x){upd(x << 1, t[x].tag); upd(x << 1 | 1, t[x].tag); t[x].tag = 0;}
	void build(int x, int l, int r){
		if(l == r){t[x].mi = val[l]; return;}
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	void modify(int x, int l, int r, int L, int R, int val){
		assert(1 <= L && L <= R && R <= n);
		if(L <= l && r <= R)return upd(x, val);
		if(t[x].tag)push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	void check(){printf("%d\n",t[1].mi >= 0);}
}T;
void adda(int i){
	int p = upper_bound(c + 1, c + n + 1, a[i]) - c - 1;
	assert(p >= 1 && p <= n);
	++c[p]; ++a[i]; p = n - p + 1;
	T.modify(1, 1, n, p, n, -1);
}
void dela(int i){
	int p = lower_bound(c + 1, c + n + 1, a[i]) - c; 
	assert(p >= 1 && p <= n);
	--c[p]; --a[i]; p = n - p + 1; 
	T.modify(1, 1, n, p, n, 1);
}
void addb(int i){if(b[i] < n)T.modify(1, 1, n, b[i] + 1, n, 1); ++b[i];}
void delb(int i){if(b[i] <= n)T.modify(1, 1, n, b[i], n, -1); --b[i];}
signed main(){
	freopen("problem.in","r",stdin);
	freopen("problem.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= m; ++i)b[i] = read();
	for(int i = 1; i <= m; ++i)++c[0], --c[min(b[i], n) + 1];
	for(int i = 1; i <= n; ++i)c[i] += c[i - 1];
	for(int i = 1; i <= n; ++i)val[i] = val[i - 1] + c[i];
	for(int i = 1; i <= n; ++i)c[i] = a[i];
	sort(c + 1, c + n + 1);
	for(int i = 1; i <= n; ++i)sa[i] = sa[i - 1] + c[n - i + 1];
	for(int i = 1; i <= n; ++i)val[i] -= sa[i];
	T.build(1, 1, n);
	int q = read();
	for(int i = 1; i <= q; ++i){
		int op = read(), pos = read();
		switch (op){
			case 1 : adda(pos); break;
			case 2 : dela(pos); break;
			case 3 : addb(pos); break;
			case 4 : delb(pos); break;
		}
		T.check(); 
	}
	return 0;
}

D. 天下第一

一个图是为链需要

  • 无环
  • 点数减去边数 =1
  • 每个点度数 2

考虑对于每个位置 i 作为右端点时,求出有多少左端点可以满足条件。

对于每个位置 i,使用双指针可以求出它往前最远到哪里满足度数 2 的条件

使用双指针 +LCT 可以求出它往前最远到哪里满足无环的条件

我们就是要求区间中点数减去边数 =1 的位置个数

发现点数 边数 1 ,线段树维护最小值以及出现次数

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
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 = 3e5 + 55;
struct LCT{
	#define son(x) (x == t[t[x].fa].son[1])
	#define lson t[x].son[0]
	#define rson t[x].son[1]
	struct node{
		int son[2], fa, tag;
	}t[maxn];
	int isroot(int x){return t[t[x].fa].son[0] != x && t[t[x].fa].son[1] != x;}
	void push_down(int x){
		if(!t[x].tag)return;
		swap(t[lson].son[0], t[lson].son[1]);
		swap(t[rson].son[0], t[rson].son[1]);
		t[lson].tag ^= 1; t[rson].tag ^= 1; t[x].tag ^= 1;
	}
	void spread(int x){
		if(!isroot(x))spread(t[x].fa);
		push_down(x);
	}
	void rotate(int x){
		int f = t[x].fa, ff = t[f].fa, s = son(x), ss = son(f);
		if(!isroot(f))t[ff].son[ss] = x;
		t[f].son[s] = t[x].son[s ^ 1];
		t[t[x].son[s ^ 1]].fa = t[x].son[s ^ 1] ? f : 0;
		t[x].son[s ^ 1] = f; t[f].fa = x; t[x].fa = ff;
	}
	void splay(int x){
		spread(x);
		for(int f = t[x].fa; !isroot(x); rotate(x) , f = t[x].fa)
		  if(!isroot(f))rotate(son(f) == son(x) ? f : x);
	}
	int access(int x){
		int p;
		for(p = 0; x; p = x, x = t[x].fa){splay(x); rson = p;}
		return p;
	}
	void makeroot(int x){x = access(x); swap(lson, rson); t[x].tag ^= 1;}
	int findroot(int x){
		access(x); splay(x); push_down(x);
		while(lson)x = lson, push_down(x);
		splay(x); return x;
	}
	bool link(int x, int y){
		if(findroot(x) == findroot(y))return false;
		makeroot(x); splay(x); t[x].fa = y; return true;
	}
	void split(int x, int y){makeroot(x); access(y); splay(y);}
	void cut(int x, int y){
		split(x, y); 
		if(t[y].son[0] != x)return;
		t[x].fa = t[y].son[0] = 0;
	}
}lct;
struct data{
	int mi, cnt;
	friend data operator + (const data &x, const data &y){
		return x.mi < y.mi ? x : (x.mi > y.mi ? y : (data){x.mi, x.cnt + y.cnt});
	}
};
struct seg{
	struct node{data val; int tag;}t[maxn << 1 | 1];
	void push_up(int x){t[x].val = t[x << 1].val + t[x << 1 | 1].val;}
	void upd(int x, int val){t[x].val.mi += val; t[x].tag += val;}
	void push_down(int x){upd(x << 1, t[x].tag); upd(x << 1 | 1, t[x].tag); t[x].tag = 0;}
	void build(int x, int l, int r){
		if(l == r){t[x].val.cnt = 1; return;}
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	void modify(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R)return upd(x, val);
		if(t[x].tag)push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	data query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].val;
		if(t[x].tag)push_down(x); int mid = (l + r) >> 1;
		if(L <= mid && R > mid)return query(x << 1, l, mid, L, R) + query(x << 1 | 1, mid + 1, r, L, R);
		if(L <= mid)return query(x << 1, l, mid, L, R);
		if(R > mid)return query(x << 1 | 1, mid + 1, r, L, R);
		assert(0);
	}
}T;
bool cmp(const int &x, const int &y){return x > y;}
vector<int>g[maxn], link[maxn];
int n, m, deg[maxn];
ll ans;
void del(int x){
	for(int v : link[x])--deg[v], --deg[x], lct.cut(x, v);
}
int main(){
	freopen("txdy.in","r",stdin);
	freopen("txdy.out","w",stdout);
	n = read(), m = read();
	for(int i = 1, u, v; i <= m; ++i){
		u = read(), v = read();
		if(u > v) swap(u, v);
		g[v].push_back(u);
	}
	for(int i = 1; i <= n; ++i)sort(g[i].begin(), g[i].end(), cmp);
	T.build(1, 1, n); 
	for(int l = 1, r = 1; r <= n; ++r){
		T.modify(1, 1, n, 1, r, 1); 
		while(!g[r].empty() && g[r].back() < l)g[r].pop_back();
		while(g[r].size() > 2){
			assert(l <= r);
			while(!g[r].empty() && g[r].back() <= l)g[r].pop_back();
			del(l); ++l;
		}
		for(int v : g[r]){
			while(deg[v] > 1)del(l), ++l;
			while(lct.findroot(v) == lct.findroot(r))del(l), ++l;
			if(v >= l){
				link[v].push_back(r);
				++deg[v]; ++deg[r];
				lct.link(v, r);
				T.modify(1, 1, n, 1, v, -1);
			}
		}
		data res = T.query(1, 1, n, l, r);
		if(res.mi == 1)ans += res.cnt;
	}
	printf("%lld\n",ans);
	return 0;
}

2023 省选联测38

挂大分

A. 失落的世界

不难想到要二分,但是如何确定开始的位置

第一感觉是正中间和一个边界,发现如果 c 较小的话可以直接二分,如果很大则需要调整

再仔细观察一下,发现如果一次二分得到 0 那么以后跳的距离都比目前的段短,于是可以二分

那么反过来想如果一直为 1

发现如果反过来考虑二分的过程,那么就确定了一个初始位置,使得任意长度都能直接二分出来

代码写的比较丑

code
#include <bits/stdc++.h>
#include "world.h"
using namespace std;
typedef long long ll;
vector<ll>len, pos;
void get_pos(ll l, ll r, ll n){
	len.clear();
	while(l <= r){
		ll mid = (l + r) >> 1;
		len.push_back(mid);
		l = mid + 1;
	}
	pos.clear();
	pos.push_back(n);
	while(len.size()){
		ll tmp = pos.back();
		if(tmp + len.back() <= n)pos.push_back(tmp + len.back());
		else if(tmp - len.back() >= 1)pos.push_back(tmp - len.back());
		else {cerr << "unexpected 1" << endl; return;}
		len.pop_back();
	}
}
ll Guess(ll n){
	ll l = 1, r = n - 1, ans = n, las;
	get_pos(l, r, n);
	las = pos.back();
	Query(las);
	while(l <= r){
		ll mid = (l + r) >> 1;
		if(las - mid >= 1 && las + mid <= n){
			ll ll = las - mid - 1, rr = n - las - mid;
			if(ll <= rr){
				las = las - mid;
				if(Query(las))r = mid - 1, ans = mid; else l = mid + 1;
			}else{
				las = las + mid;
				if(Query(las))r = mid - 1, ans = mid; else l = mid + 1;
			}
		}else if(las - mid >= 1){
				las = las - mid;
				if(Query(las))r = mid - 1, ans = mid; else l = mid + 1;
		}else if(las + mid <= n){
				las = las + mid;
				if(Query(las))r = mid - 1, ans = mid; else l = mid + 1;
		}else {cerr << "unexpected 2" << endl; break;}
	}
	return ans;
}

B. 谜域的界外

发现物品数量是 log 级别的,于是暴力搞就行

容易发现选择的必然是一段连续区间,所以 fl,r 表示从 lr 选择所有物品需要的时间再加上 min(dis(l,s),dis(r,s))

赛时没认真想,出现了从一个位置向两边走的分裂人

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

using namespace std;

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

ll read(){
	ll x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
int cnt;
ll ax, ay, bx, by, t;	
struct node{
	ll x, y;
}d[505], s, p[505];
ll dis(const node &x, const node &y){return abs(x.x - y.x) + abs(x.y - y.y);}
ll f[505][505];
int main(){
	freopen("reaches.in","r",stdin);
	freopen("reaches.out","w",stdout);
	d[0].x = read(); d[0].y = read();
	ax = read(), ay = read(), bx = read(), by = read();
	s.x = read(), s.y = read(), t = read();
	int tot = 0;
	while((d[tot].x - s.x <= t || d[tot].y - s.y <= t)){
		if(d[tot].x > 2e16 || d[tot].y > 2e16)break;
		d[tot + 1] = {ax * d[tot].x + bx, ay * d[tot].y + by};
		++tot;
	}
	for(int i = 0; i <= tot; ++i)if(dis(d[i], s) <= t)p[++cnt] = d[i];
	int ans = 0;
	for(int len = 2; len <= cnt; ++len)
		for(int l = 1, r = len; r <= cnt; ++l, ++r)
			f[l][r] = min(f[l + 1][r] + dis(p[l + 1], p[l]), f[l][r - 1] + dis(p[r], p[r - 1]));
	for(int len = 1; ans == len - 1 && len <= cnt; ++len)
		for(int l = 1, r = len; ans < len && r <= cnt; ++l, ++r)
			if(f[l][r] + min(dis(s, p[l]), dis(s, p[r])) <= t)ans = len;
	printf("%d\n",ans);
	return 0;
}

C. 聚合的塔尖

赛时拓扑排序时候没有把初始值设成 inf 寄掉了。。。。

正解根号分治

预处理到每个点最远的 n 个点

询问 k<n 的直接查询

否则建反图跑最长路可以得到答案

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 = 2e5 + 55, inf = INT_MAX;
int n, m, q, sq, stay[maxn], dis[maxn];
bool ban[maxn], vis[maxn];
vector<int>g[maxn], e[maxn];
vector<pii>rem[maxn], tmp, res;
void tp(int s){
	for(int x = s; x >= 1; --x)dis[x] = -inf;
	int ans = -1; dis[s] = 0;
	for(int x = s; x >= 1; --x){
		if(!ban[x])ans = max(ans, dis[x]);
		for(int y : g[x]){
			if(y > s)break;
			dis[y] = max(dis[y], dis[x] + 1);
		}
	}
	printf("%d\n",ans);
}
int query(int s){
	for(pii v : rem[s])if(!ban[v.second])return v.first;
	return -1;
}
void merge(int x, int y){
	res.clear(); tmp.clear();
	tmp = rem[y];
	for(int i = 0; i < tmp.size(); ++i)++tmp[i].first;
	int px = 0, py = 0, sx = rem[x].size(), sy = tmp.size();
	while(px < sx && py < sy){
		if(rem[x][px].first > tmp[py].first)res.push_back(rem[x][px]), ++px;
		else res.push_back(tmp[py]), ++py;
	}
	while(px < sx)res.push_back(rem[x][px]), ++px;
	while(py < sy)res.push_back(tmp[py]), ++py;
	rem[x].clear();
	for(pii v : res)if(!vis[v.second]){
		rem[x].push_back(v);
		vis[v.second] = true;
	}
	for(pii v : res)vis[v.second] = false;
	while(rem[x].size() > sq + 5)rem[x].pop_back();
}
void pre(){
	for(int i = 1; i <= n; ++i)rem[i].push_back(pii(0, i));
	for(int i = 1; i <= n; ++i)for(int j : e[i])merge(j, i);
}
int main(){
	freopen("spire.in","r",stdin);
	freopen("spire.out","w",stdout);
	n = read(), m = read(), q = read();
	for(int i = 1, u, v; i <= m; ++i){
		u = read(), v = read();
		if(u < v)swap(u, v);
		g[u].push_back(v);
		e[v].push_back(u);
	}
	for(int i = 1; i <= n; ++i)sort(g[i].begin(), g[i].end());
	sq = sqrt(n); pre();
	for(int i = 1; i <= q; ++i){
		int s = read(), k = read();
		for(int j = 1; j <= k; ++j)ban[stay[j] = read()] = true;
		if(k > sq)tp(s); else printf("%d\n",query(s));
		for(int j = 1; j <= k; ++j)ban[stay[j]] = false;
	}
	return 0;
}

D. 沉眠的回声

赛时暴力码的没啥问题,但是吧,跑的样例不是我想让他跑的,白白调了半年。。。。。。。

我TM怎么这么shabi

乍一看题感觉很懵逼,这两个人怎么通过知道和不知道判断数的?

从头思考一下。

首先,两个人根据下界和自己知道的信息,能够确定出可能的答案的集合。

他们能够用来筛选答案的信息只有对方的回答。

所以他们一定是通过之前另外一个人的回答去掉集合中与当前情况不符的答案。

当一个人的集合中只有唯一的一种答案时,他就会“知道”了。

这大概是一个递归的过程。

我们需要实现一个函数,能够对确定的 nm 返回前若干次两个人的回答。

具体一点就是先通过已知信息(和或积)确定初始答案集合。

如果只需要一个回答,那么直接返回集合中是否只有一组解。

否则的话两个人开始依次筛选自己的答案集合,通过递归调用该函数删去不符合的部分。

每一轮的回答也是看自己的答案集合中是否只有一个数。

函数之外枚举答案进行检查,看看返回的回答是否为 t0 后面两个 1 即可。

然后加上记忆化就能过了。

code
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
int s, fi;
unordered_map<int, vector<bool>>mp[505][505];
vector<bool> work(int n, int m, int round){
	if(mp[n][m].count(round))return mp[n][m][round];
	vector<pii>x1, x2;		
	int x = n * m;
	for(int a = s; a <= x; ++a)if(x % a == 0){
		int b = x / a; if(a > b)break;
		x1.push_back(pii(a, b));
	}
	x = n + m;
	for(int a = s; a <= x; ++a){
		int b = x - a; if(b < a)break;
		x2.push_back(pii(a, b));
	}
	vector<bool>ans;
	if(fi & 1)ans.push_back(x1.size() == 1);
	else ans.push_back(x2.size() == 1);
	if(round == 1)return ans;
	vector<pii>tmp;
	for(int i = 2; i <= round; ++i){
		if((i & 1) == (fi & 1)){
			for(pii x : x1)if(work(x.first, x.second, i - 1) == ans)tmp.push_back(x);
			swap(tmp, x1); tmp.clear(); ans.push_back(x1.size() == 1);
		}else{
			for(pii x : x2)if(work(x.first, x.second, i - 1) == ans)tmp.push_back(x);
			swap(tmp, x2); tmp.clear(); ans.push_back(x2.size() == 1);
		}
	}
	return mp[n][m][round] = ans;
}
char name[10];
int main(){ 
	int round;
	scanf("%d%s%d",&s, name, &round);
	if(name[0] == 'A')fi = 1;
	else fi = 2;
	for(int sum = s + s; true; ++sum){
		for(int n = s; n <= sum; ++n){
			int m = sum - n;
			if(m < n)break;
			vector<bool>ans = work(n, m, round + 2);
			for(int i = 0; i < round; ++i)if(ans[i])goto X;
			if(ans[round] && ans[round + 1]){
				printf("%d %d\n", n, m); return 0;
			}
			X:;
		}
	}
	return 0;
}

2023 省选联测39

A. 伯爵

不懂

image

B. 给给的游戏

image

照着题解,完成证明就知道怎么做

证明问题1第3条

考虑不满足(1) (2),则 v1>i+1 并且 i+1>vi

找到第一个 x 使得 i+1>x 必然能插进去

问题2

哈密顿回路构造

因为由哈密顿路径构造而来,所以前面存在指向 i 的边 vx>i

考虑 i>vx+1 可以直接插进去,否则依次向后,第一个 i>vyy 必然能插入

如果不存在 y ,则之前的回路中所有点都指向 i, 考虑 i+1i>i+1 一块尝试插进去

如果到最后不能找到完整的哈密顿回路,则存在两个点集 S,T 其之间的边都是 S>T

与强连通的条件矛盾,故一定有解

关于按照度数排序

如果不强连通,那么对缩点后的图重新编号,必然存在唯一的编号方案,使得编号小的指向编号大的

那么编号小的强连通分量里的点出度至少为编号大的强连通分量的点数,而编号大的强连通分量里的点的出度一定小于该强连通分量的点的数量

所以按照度数排序后同一强连通分量的点是相邻的,且两个强连通分量的交界出满足前面所有点都指向后面所有点

代码感觉比较ex,所以直接参考标程写的

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 < 'A' || c > 'F'))c = getchar();
	if(isdigit(c))return (c ^ 48);
	return c - 'A' + 10;
}
void print(int x){
	if(x < 10)putchar(x + '0');
	else putchar(x - 10 + 'A');
}
const int maxn = 5055;
bool mp[maxn][maxn], res[maxn][maxn], tag[maxn];
int n, deg[maxn], p[maxn], in[maxn], out[maxn], link[maxn], id[maxn], vec[maxn], q[maxn];
bool cmp(const int &x, const int &y){return deg[x] > deg[y];}

void insert(int l, int now, int r){
	link[0] = 0; int tail = 1;
	for(int i = 1; i <= l; ++i)link[++link[0]] = vec[tail++]; 
	link[++link[0]] = now;
	for(int i = 1; i <= r; ++i)link[++link[0]] = vec[tail++]; 
	for(int i = 0; i <= link[0]; ++i)vec[i] = link[i];
}
void insert2(int l, int fr, int ed){
	link[0] = 0; int tail = 1, r = fr - l - 1;
	for(int i = 1; i <= l; ++i)link[++link[0]] = q[tail++];
	for(int i = fr; i <= ed; ++i)link[++link[0]] = vec[i];
	for(int i = 1; i <= r; ++i)link[++link[0]] = q[tail++];
	for(int x = 0; x <= link[0]; ++x)q[x] = link[x];
}
void solve3(int u, int v){
	mp[u][v] ^= 1; mp[v][u] ^= 1;
	link[0] = 0; vec[0] = 0;
	--deg[u]; ++deg[v];
	int ru = u, rv = v;
	if(deg[u] < deg[v])swap(u, v);
	for(int i = 1; i <= p[0]; ++i)if(p[i] != u && p[i] != v)link[++link[0]] = p[i];
	bool flag1 = false, flag2 = false;
	for(int i = 1; i <= link[0]; ++i){
		if(deg[u] >= deg[link[i]] && !flag1)flag1 = true, vec[++vec[0]] = u;
		if(deg[v] >= deg[link[i]] && !flag2)flag2 = true, vec[++vec[0]] = v;
		vec[++vec[0]] = link[i];
	}
	if(!flag1)vec[++vec[0]] = u;
	if(!flag2)vec[++vec[0]] = v;
	link[0] = 0; flag1 = flag2 = false;
	for(int i = 1; i < vec[0]; ++i){
		link[i] = link[i - 1];
		if(vec[i] == u || vec[i] == v){
			if(vec[i] == u)flag1 = true; 
			else flag2 = true;
			for(int x = 1; x < i; ++x)if(mp[vec[x]][vec[i]])--link[i];
			for(int x = i + 1; x <= vec[0]; ++x)if(mp[vec[i]][vec[x]])++link[i];
		}else{
			link[i] -= in[vec[i]] - out[vec[i]];
			link[i] += (mp[u][vec[i]] && id[u] < id[vec[i]]);
			link[i] += (mp[v][vec[i]] && id[v] < id[vec[i]]);
			link[i] -= (mp[vec[i]][u] && id[u] > id[vec[i]]);
			link[i] -= (mp[vec[i]][v] && id[v] > id[vec[i]]);
			link[i] -= (mp[u][vec[i]] && flag1);
			link[i] -= (mp[v][vec[i]] && flag2);
			link[i] += (mp[vec[i]][u] && !flag1);
			link[i] += (mp[vec[i]][v] && !flag2);
		}
		if(link[i] == i * (vec[0] - i)){
			res[u][v] = res[v][u] = 0;
			break;
		}
	}
	++deg[ru]; --deg[rv];
	mp[u][v] ^= 1; mp[v][u] ^= 1;
}
void solve(){
	scanf("%d",&n);
	for(int i = 1; i < n; ++i){
		int up = (i + 3) / 4;
		for(int j = 1; j <= up; ++j){
			int now = read();
			for(int k = j * 4 - 3; k <= min(j * 4, i); ++k){
				mp[i + 1][k] = (now & 1); mp[k][i + 1] = !(now & 1);
				now >>= 1;
			}
		}
	}
	if(n == 2){
		putchar('0');
		putchar('\n');
		return;
	}
	for(int i = 1; i <= n; ++i)deg[i] = 0;
	for(int i = 1; i <= n; ++i)
		for(int j = i + 1; j <= n; ++j)
			if(mp[i][j]) ++deg[i]; else ++deg[j];
	for(int i = 1; i <= n; ++i)p[i] = i;
	sort(p + 1, p + n + 1, cmp);
	for(int i = 1; i <= n; ++i)id[p[i]] = i;
	for(int i = 1; i <= n; ++i)in[i] = out[i] = 0;
	bool flag = false; link[0] = 0;
	p[0] = n;
	for(int i = 1; i < n; ++i){
		link[i] = link[i - 1];
		for(int j = 1; j < i; ++j)if(mp[p[j]][p[i]])++in[p[i]];
		for(int j = i + 1; j <= n; ++j)if(mp[p[i]][p[j]])++out[p[i]];
		link[i] += out[p[i]] - in[p[i]];
		tag[i] = (link[i] < i * (n - i));
		if(!tag[i])flag = true;
	}
	if(flag){
		int l = 1, r = n;
		for(int i = n; i >= 1; --i)if(!tag[i])l = i;
		for(int i = 1; i < n; ++i)if(!tag[i])r = i + 1;
		for(int i = 1; i <= l; ++i)
			for(int j = r; j <= n; ++j)
				if(p[i] > p[j])res[p[i]][p[j]] = 1;
				else res[p[j]][p[i]] = 1;
	}else{
		vec[vec[0] = 1] = 1;
		for(int i = 1; i <= n; ++i)
			for(int j = i + 1; j <= n; ++j)
				res[j][i] = 1;
		for(int i = 2; i <= n; ++i){
			if(mp[i][vec[1]])insert(0, i, vec[0]);
			else if(mp[vec[vec[0]]][i])insert(vec[0], i, 0);
			else{
				for(int x = 1; x < vec[0]; ++x)
					if(mp[vec[x]][i] && mp[i][vec[x + 1]]){insert(x, i, vec[0] - x); break;}
			}
		}
		int pos; q[0] = 0;
		for(int i = 2; i <= n; ++i)if(mp[vec[i]][vec[1]])pos = i;
		for(int i = 1; i <= pos; ++i)q[++q[0]] = vec[i];
		for(int i = pos + 1; i <= n; ++i){
			for(int x = 1; x < pos; ++x)
				if(mp[q[x]][vec[pos + 1]] && mp[vec[i]][q[x + 1]]){insert2(x, pos + 1, i); pos = i; break;}
			if(i == pos)continue;
			if(mp[q[pos]][vec[pos + 1]] && mp[vec[i]][q[1]])insert2(pos, pos + 1, i);
		}
		for(int i = 1; i < n; ++i)solve3(q[i], q[i + 1]);
		solve3(q[n], q[1]);
	}
	for(int i = 2; i <= n; ++i, putchar('\n'))
		for(int j = 0; j < i - 1 ; j += 4)
			print(res[i][j + 4] * 8 + res[i][j + 3] * 4 + res[i][j + 2] * 2 + res[i][j + 1]);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			res[i][j] = 0;
}

int main(){
	freopen("geigei.in","r",stdin);
	freopen("geigei.out","w",stdout);
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

C. 一定要关掉

水题

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
string s, t, tmp;
map<string, bool>mp;
void solve(int id){
	mp.clear();
	int n; cin >> n; cin >> t;
	tmp = t; reverse(tmp.begin(), tmp.end());
	if(tmp < t)t = tmp;
	for(int i = 1; i <= n; ++i){
		cin >> s; tmp = s; reverse(tmp.begin(), tmp.end());
		if(tmp < s)s = tmp;
		mp[s] = true;
	}
	if(mp[t]){cout << 1 << endl; return;}
	if(t == "DLR" && mp["LDR"] && mp["DRL"]){cout << 2 << endl; return;}
	if(t == "DRL" && mp["LDR"] && mp["DLR"]){cout << 2 << endl; return;}
	cout << "shut up" << endl;
}
int main(){
	freopen("shut.in","r",stdin);
	freopen("shut.out","w",stdout);
	int t; cin >> t;
	for(int i = 1; i <= t; ++i)solve(i);
	return 0;
}

D. 种树

image

代码不想打了,补充一下题解吧

差分前缀 min, 正负值分开维护,遍历区间内正值,二分到下一个负值,在区间内能消去多少消去多少,超过区间的,把剩余正值放到区间外

后缀 min 同理

2023 省选联测40

A. 后浪

水题

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;

int n, sum, mxdt, a[maxn], pre0[maxn], suf1[maxn];

int main(){
	freopen("wave.in","r",stdin);
	freopen("wave.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i){
		int m = read();
		for(int j = 1; j <= m; ++j)a[j] = read();
		for(int j = 1; j <= m; ++j)pre0[j] = pre0[j - 1] + (a[j] == 0);
		suf1[m + 1] = 0; 
		for(int j = m; j >= 1; --j)suf1[j] = suf1[j + 1] + a[j];
		int tmp = max(pre0[m], suf1[1]), tmp2 = 0;
		sum += tmp;
		for(int j = 0; j <= m; ++j)tmp2 = max(tmp2, pre0[j] + suf1[j + 1]);
		mxdt = max(mxdt, tmp2 - tmp);
	}
	printf("%d\n",sum + mxdt);
	return 0;
}

B. 脑力

直接 3n 卡常好像能过

但是我只有 95

考虑容斥出节点数

image

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 = 1005, mod = 998244353;
int n, m, i26, pi26[maxn], cnt[8005], p[20]; 
char s[15][maxn];
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;
}
int main(){
	// freopen("brainpower.in","r",stdin);
	// freopen("brainpower.out","w",stdout);
	scanf("%d%d",&n, &m);
	i26 = qpow(26, mod - 2); pi26[0] = 1;
	for(int i = 1; i <= n; ++i)pi26[i] = 1ll * pi26[i - 1] * i26 % mod;
	for(int i = 1; i <= n; ++i)scanf("%s", s[i] + 1);
	int ans = 1, tot = 0;
	for(int st = 1; st < (1 << n); ++st){
		cnt[st] = cnt[st >> 1] + (st & 1); 
		tot = 0;
		for(int i = 1; i <= n; ++i)if((st >> (i - 1)) & 1)p[++tot] = i;
		int res = (cnt[st] & 1) ? 1 : mod - 1;
		for(int i = 1; i <= m; ++i){
			char now = '?'; int cw = 0;
			for(int k = 1; k <= tot; ++k)if(s[p[k]][i] != '?'){
				if(now == '?')now = s[p[k]][i];
				else if(now != s[p[k]][i])goto X;
			}else ++cw;
			res = 1ll * res * pi26[cw - (now == '?')] % mod;
			ans = (ans + res) % mod;
		}
		X:;
	}
	printf("%d\n",ans);
	return 0;
}

C. 道路

按照线段树的编号规则直接模拟,然后加减只在当前位操作,最后一遍进位得到值

答案是向上跳若干层,然后横向走,枚举即可

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 = 1e5 + 55;
char s1[maxn], s2[maxn];
int a[maxn], b[maxn], la, lb;
int n, m;

int main(){
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	scanf("%s%s",s1 + 1, s2 + 1);
	n = strlen(s1 + 1); m = strlen(s2 + 1);
	a[la = 1] = 1; 
	for(int i = 1; i <= n; ++i){
		if(s1[i] == '1')a[++la] = 0;
		if(s1[i] == '2')a[++la] = 1;
		if(s1[i] == 'L')--a[la];
		if(s1[i] == 'R')++a[la];
		if(s1[i] == '0')a[la - 1] += a[la] >> 1, --la;
	}
	for(int i = la; i > 1; --i)a[i - 1] += a[i] >> 1, a[i] = abs(a[i]) % 2;
	
	b[lb = 1] = 1;
	for(int i = 1; i <= m; ++i){
		if(s2[i] == '1')b[++lb] = 0;
		if(s2[i] == '2')b[++lb] = 1;
		if(s2[i] == 'L')--b[lb];
		if(s2[i] == 'R')++b[lb];
		if(s2[i] == '0')b[lb - 1] += b[lb] >> 1, --lb;
	}
	for(int i = lb; i > 1; --i)b[i - 1] += b[i] >> 1, b[i] = abs(b[i]) % 2;
	int l = min(la, lb), ans = INT_MAX, dt = 0;
	for(int i = 1; i <= l && abs(dt) < (1 << 20); ++i){
		dt = (dt << 1) + a[i] - b[i];
		ans = min(ans, abs(dt) + 2 * (l - i));	 
	}
	printf("%d\n",ans + abs(la - lb));
	return 0;
}

D. 光学实验室

https://www.luogu.com.cn/problem/CF578F

code

posted @   Chen_jr  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示