2023冲刺清北营

2023冲刺清北营1

A. string

虽然但是,根号能过,分长度 hash 处理即可

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;
}
const int maxn = 3e5 + 55, mod = 1004535809, base = 29;
int n, q, bp[maxn], ans[maxn], len[maxn];
char s[maxn];
struct que{int l, r, k;}Q[maxn];
struct node{int id, hash, op;};
vector<node>qry[maxn];
vector<int>h[maxn], rlq[maxn];
int get_hash(int x, int l, int r){return (h[x][r] - 1ll * bp[r - l + 1] * h[x][l - 1] % mod + mod) % mod;}
unordered_map<int, int>mp, tmp;
void solve(int L){
	mp.clear();
	for(int i = 1; i <= n; ++i){
		for(int l = 1, r = L; r <= len[i]; ++l, ++r)++mp[get_hash(i, l, r)];
		for(node x : qry[i])ans[x.id] += x.op * mp[x.hash];
	}
}
int main(){
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	n = read(), q = read(); int mxl = 0;
	for(int i = 1; i <= n; ++i){
		scanf("%s",s + 1);
		len[i] = strlen(s + 1);
		mxl = max(mxl, len[i]);
		h[i].resize(len[i] + 1);
		for(int j = 1; j <= len[i]; ++j)h[i][j] = (1ll * h[i][j - 1] * base + s[j] - 'a' + 1) % mod;
	}
	bp[0] = 1; for(int i = 1; i <= mxl; ++i)bp[i] = 1ll * base * bp[i - 1] % mod;
	for(int i = 1; i <= q; ++i){Q[i].l = read(), Q[i].r = read(), Q[i].k = read(); rlq[len[Q[i].k]].push_back(i);}
	for(int i = 1; i <= mxl; ++i)if(rlq[i].size()){
		for(int x : rlq[i]){
			qry[Q[x].l - 1].push_back(node{x, h[Q[x].k][len[Q[x].k]], -1});
			qry[Q[x].r].push_back(node{x, h[Q[x].k][len[Q[x].k]], 1});
		}
		solve(i);
		for(int x : rlq[i]){
			qry[Q[x].l - 1].clear();
			qry[Q[x].r].clear();
		}
	}
	for(int i = 1; i <= q; ++i)printf("%d\n",ans[i]);
	return 0;
}

B. poker

你好,大模拟。

标程是错的,数据是假的,啊哈哈

不过现在对了。

你需要实现判断大小,然后暴力枚举得到胜负平的场次,然后就是枚举金额决策。

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

using namespace std;
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;
}
int base, mi, mx; double p;
struct node{
	int val, col;
	friend bool operator < (const node &x, const node &y){return x.val < y.val;}
	void init(int x){val = x % 13 + 1; col = x / 13;}
};
struct key{
	int level, val;
	friend bool operator > (const key &x, const key &y){return x.level < y.level || (x.level == y.level && x.val > y.val);}
	friend bool operator == (const key &x, const key &y){return x.level == y.level && x.val == y.val;}
}my, you;
struct card{
	node p[5];
	bool check_shun_zi(){
		bool flag = true;
		for(int i = 1; i <= 4 && flag; ++i)if(p[i].val != p[0].val + i)flag = false;
		if(flag)return true;
		if(p[0].val == 1){
			for(int i = 1; i <= 4; ++i)if(p[i].val != 9 + i)return false;
			return true;
		}
		return false;
	}
	inline bool check_same_col(){
		for(int i = 1; i <= 4; ++i)if(p[i].col != p[0].col)return false;
		return true;
	}
	inline bool check_four(){
		if(p[0].val == p[1].val)return p[2].val == p[3].val && p[2].val == p[0].val;
		return p[1].val == p[2].val && p[2].val == p[3].val && p[3].val == p[4].val;
	}
	inline bool check_hulu(){return p[0].val == p[1].val && p[4].val == p[3].val && (p[2].val == p[1].val || p[2].val == p[3].val);}
	inline bool check_san_tiao(){return (p[2].val == p[1].val && p[2].val == p[0].val) || (p[2].val == p[3].val && p[2].val == p[4].val) || (p[2].val == p[1].val && p[2].val == p[3].val);}
	inline bool check_two_pair(){
		int cnt = 1;
		for(int i = 1; i < 5; ++i)cnt += p[i].val != p[i - 1].val;
		return cnt == 3;
	}
	inline bool check_one_pair(){
		int cnt = 1;
		for(int i = 1; i < 5; ++i)cnt += p[i].val != p[i - 1].val;
		return cnt == 4;
	}
	inline void change_A(){for(int i = 0; i < 5; ++i)if(p[i].val == 1)p[i].val = 14; sort(p, p + 5);}
	inline int get_val_shun_zi(){
		if(p[0].val == 1 && p[4].val == 13)return 10;
		return p[0].val;
	}
	inline int get_val_four(){
		change_A();
		if(p[0].val == p[1].val)return p[0].val * 15 + p[4].val;
		return p[0].val + p[4].val * 15;
	}
	inline int get_val_hulu(){
		change_A();
		if(p[2].val == p[1].val)return p[2].val * 15 + p[3].val;
		else return p[2].val * 15 + p[1].val;
	}
	inline int get_val_high_card(){
		change_A(); int res = 0;
		for(int i = 4; i >= 0; --i)res = res * 15 + p[i].val;
		return res;
	}
	inline int get_val_san_tiao(){
		change_A(); int res = p[2].val;
		if(p[2].val == p[1].val && p[2].val == p[0].val)return (res * 15 + p[4].val) * 15 + p[3].val;
		if(p[2].val == p[1].val && p[2].val == p[3].val)return (res * 15 + p[4].val) * 15 + p[0].val;
		return (res * 15 + p[1].val) * 15 + p[0].val;
	}
	inline int get_val_pair(){
		change_A();
		int res = 0;
		for(int i = 4; i > 0; --i)if(p[i].val == p[i - 1].val)res = res * 15 + p[i].val;
		if(p[4].val != p[3].val)res = res * 15 +  p[4].val;
		for(int i = 3; i >= 1; --i)if(p[i].val != p[i - 1].val && p[i].val != p[i + 1].val)res = res * 15 +  p[i].val;
		if(p[0].val != p[1].val)res = res * 15 +  p[0].val;
		return res;
	}
	key get_level(){
		key ans; sort(p, p + 5);
		if(check_shun_zi()){
			ans.val = get_val_shun_zi();
			if(check_same_col())ans.level = 2; 
			else ans.level = 6; 
		}else{
			if(check_four()){ans.level = 3; ans.val = get_val_four();}
			else if(check_hulu()){ans.level = 4; ans.val = get_val_hulu();}
			else if(check_same_col()){ans.level = 5; ans.val = get_val_high_card();}
			else if(check_san_tiao()){ans.level = 7; ans.val = get_val_san_tiao();}
			else if(check_two_pair()){ans.level = 8; ans.val = get_val_pair();}
			else if(check_one_pair()){ans.level = 9; ans.val = get_val_pair();}
			else {ans.level = 10; ans.val = get_val_high_card();}
		}
		return ans;
	}
}tmp;
int m[5], a[10];
bool vis[55];
key get_val(){
	key res; res.level = 0x3f;
	for(int i = 0; i < 7; ++i)
		for(int j = i + 1; j < 7; ++j){
			for(int k = 0; k < 7; ++k)if(k != i && k != j)tmp.p[k - (k > i) - (k > j)].init(a[k]);
			key now = tmp.get_level();
			if(now > res)res = now;
		}
	return res;
}
int win = 0, lose = 0, equ = 0;
void sol(){
	a[5] = m[1]; a[6] = m[2]; my = get_val();
	for(int i = 0; i < 52; ++i)if(!vis[i]){
		vis[i] = true;
		for(int j = i + 1; j < 52; ++j)if(!vis[j]){
			vis[j] = true;
			a[5] = i; a[6] = j; you = get_val();
			if(my > you)++win;
			else if(my == you)++equ;
			else ++lose;
			vis[j] = false;
		}
		vis[i] = false;
	}
}
int main(){
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	base = read(); mi = read(); mx = read(); scanf("%lf",&p);
	m[1] = read(); vis[m[1]] = true;
	m[2] = read(); vis[m[2]] = true;
	for(int i = 0; i < 5; ++i){
		a[i] = read();
		if(a[i] != -1)vis[a[i]] = true;
	}
	if(a[3] == -1){
		for(int i = 0; i < 52; ++i)if(!vis[i]){
			vis[i] = true;
			for(int j = i + 1; j < 52; ++j)if(!vis[j]){
				vis[j] = true;
				a[3] = i; a[4] = j; sol();
				vis[j] = false;
			}
			vis[i] = false;
		}		
	}else if(a[4] == -1){
		for(int i = 0; i < 52; ++i)if(!vis[i]){
			vis[i] = true;
			a[4] = i; sol();
			vis[i] = false;
		}
	}else sol();
	cerr << win << " " << equ << " " << lose << endl;
	double qw = -1e18; int ans = 0;
	for(int y = mx; y >= mi; --y){
		double a = (double) y / base;
		double now = a * p * base + (1.0 - a * p) * (win * (a + 1.0) * base - lose * a * base) / (win + lose + equ);
		if(now > qw){qw = now; ans = y;}
	}
	if(qw > 0)printf("%d\n",ans);
	else printf("0\n");
	return 0;
}

C. array

虽然但是,数据水,莫队能过

开桶记录每个质因子出现次数,一个质因子出现/消失时候暴力标记其倍数

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;
}
const int maxn = 1e5 + 55;
int n, m, q, c, a[maxn], prime[maxn], mi[maxn], cnt, ans[maxn], bl[maxn], B, res, tub[maxn], buc[maxn];
bool flag[maxn], vis[maxn];
void init(){
	for(int i = 2; i <= m; ++i){
		if(!flag[i])prime[++cnt] = i, mi[i] = i;
		for(int j = 1; j <= cnt && i * prime[j] <= m; ++j){
			flag[i * prime[j]] = true;
			mi[i * prime[j]] = prime[j];
			if(i % prime[j] == 0)break;
		}
	}
}
struct node{
	int l, r, id;
	friend bool operator < (const node &x, const node &y){
		return bl[x.l] == bl[y.l] ? x.r < y.r : x.l < y.l; 
	}
}d[maxn];
vector<int>pri[maxn];
void Add(int p){
	for(int i = p; i <= c; i += p){
		if(buc[i] == 0)--res;
		++buc[i];
	}
}
void Del(int p){
	for(int i = p; i <= c; i += p){
		--buc[i];
		if(buc[i] == 0)++res;
	}
}
void add(int pos){
	for(int p : pri[pos]){
		if(tub[p] == 0)Add(p);
		++tub[p];
	}
}
void del(int pos){
	for(int p : pri[pos]){
		--tub[p];
		if(tub[p] == 0)Del(p);
	}
}
int main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	n = read(), q = read(), c = read();
	for(int i = 1; i <= n; ++i)a[i] = read(), m = max(m, a[i]);
	init();
	for(int i = 1; i <= n; ++i){
		int x = a[i];
		while(x != 1){
			int now = mi[x]; pri[i].push_back(now);
			while(x % now == 0)x /= now;
		}
	}
	for(int i = 1; i <= q; ++i)d[i].l = read(), d[i].r = read(), d[i].id = i;
	B = sqrt(n); for(int i = 1; i <= n; ++i)bl[i] = (i + B - 1) / B;
	sort(d + 1, d + q + 1);
	int l = 1, r = 0; res = c;
	for(int i = 1; i <= q; ++i){
		while(l > d[i].l)add(--l);
		while(r < d[i].r)add(++r);
		while(l < d[i].l)del(l++);
		while(r > d[i].r)del(r--);
		ans[d[i].id] = res;
	}
	for(int i = 1; i <= q; ++i)printf("%d\n",ans[i]);
	return 0;
}

2023冲刺清北营2

A. 操作

如果序列确定,那么得到

Ei=Pi(Ei1+Ai)+(1Pi)BiEi1

Ei=(Pi+BiPiBi)Ei1+PiAi

可以简单写成

Ei=kiEi1+bi

考虑 bi 最终的期望,枚举他在序列的哪个位置

k!(n1k)![xk]ij(1+kjx)

后面的东西用分治 + FFT 解决

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, 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];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int deg, rev[maxn], w[maxn];
void init(int len){
	deg = 1; while(deg < len)deg <<= 1;
	w[0] = 1; w[1] = qpow(3, (mod - 1) / deg);
	for(int i = 1; i < deg; ++i){
		rev[i] = rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= (deg >> 1);
		w[i] = 1ll * w[i - 1] * w[1] % mod;
	}
}
struct poly{
	vector<int>f;
	int &operator [](const int &i){return f[i];}
	int operator [](const int &i)const{return f[i];}
	void set(int n){f.resize(n);}
	int si(){return f.size();}
	int si()const{return f.size();}
	void ntt(){
		for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
		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 = 1ll * f[j + hl] * w[deg / l * (j - i)] % mod;
					f[j] = (x + y) % mod; f[j + hl] = (mod + x - y) % mod; 
				}
	}
	void intt(){
		ntt(); reverse(f.begin() + 1, f.end()); int Inv = qpow(deg, mod - 2);
		for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
	}
	friend poly operator * (poly x, poly y){
		int len = x.si() + y.si() - 1;
		if(len <= 200){
			poly res; res.set(len);
			for(int i = 0; i < x.si(); ++i)
				for(int j = 0; j < y.si(); ++j)
					add(res[i + j], 1ll * x[i] * y[j] % mod);
			return res;
		}
		init(len);
		x.set(deg); y.set(deg); x.ntt(); y.ntt();
		for(int i = 0; i < deg; ++i)x[i] = 1ll * x[i] * y[i] % mod;
		x.intt(); x.set(len + 1);
		return x;
	}
	friend poly operator + (poly x, const poly &y){
		x.set(max(x.si(), y.si()));
		for(int i = 0; i < y.si(); ++i)add(x[i], y[i]);
		return x;
	}
	void clear(){f.resize(0);}
}f[maxn], g[maxn];
void solve(int l, int r){
	if(l == r)return;
	int mid = (l + r) >> 1;
	solve(l, mid); solve(mid + 1, r);
	g[l] = g[l] * f[mid + 1] + f[l] * g[mid + 1];	
	f[l] = f[l] * f[mid + 1];
	f[mid + 1].clear(); g[mid + 1].clear();
}
int fac[maxn];
int main(){
	freopen("operation.in","r",stdin);
	freopen("operation.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i){
		int p = read(), x = read(), y = read();
		f[i].set(2); f[i][0] = 1; f[i][1] = ((p + y - 1ll * p * y) % mod + mod) % mod;
		g[i].set(1); g[i][0] = 1ll * x * p % mod; 
	}
	solve(1, n);
	fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	int res = 0;
	for(int i = 0; i < n; ++i)add(res, 1ll * g[1][i] * fac[i] % mod * fac[n - 1 - i] % mod);
	res = 1ll * res * qpow(fac[n], mod - 2) % mod;
	printf("%d\n",res);	
	return 0;
}

B. 选数

fi 表示选择的数都为 i 的倍数的方案数,简单容斥可以得到答案

考虑如何求 fi

设定阈值 B

i<=Bfwt 计算,然后容斥掉不合法的

否则直接枚举, k=4 时候折半一下

具体容斥就。。。

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, mod = 998244353, inv6 = 166374059, inv3 = 332748118, inv2 = 499122177, B = 50;
int n, k, s, mx, deg, tub[maxn], f[maxn], g[maxn], half[maxn];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int ch3(int x){return 1ll * x * (x - 1) % mod * (x - 2) % mod * inv6 % mod;}
int ch2(int x){return 1ll * x * (x - 1) / 2 % mod;}

void fwt_xor(int f[], int op){
	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) * op % mod;
				f[j + hl] = 1ll * (x - y + mod) * op % mod;
			}
}
int sol1(int x, int t){
	int sum = 0;
	for(int i = 0; i < deg; ++i)g[i] = i % x == 0 ? tub[i] : 0;
	for(int i = x; i <= mx; i += x)sum += g[i];
	fwt_xor(g, 1);
	if(t == 3){
		for(int i = 0; i < deg; ++i)g[i] = 1ll * g[i] * g[i] % mod * g[i] % mod;
		fwt_xor(g, inv2); int res = g[s];
		if(s % x == 0 && tub[s]){
			if(sum > 1)res = (res - 3ll * (sum - 1) * tub[s]) % mod;
			res = (res - tub[s]) % mod;
			res = (res + mod) % mod;
		}
		res = 1ll * res * inv6 % mod;
		return res;
	}else{
		for(int i = 0; i < deg; ++i)g[i] = 1ll * g[i] * g[i] % mod * g[i] % mod * g[i] % mod;
		fwt_xor(g, inv2); int res = g[s];
		for(int i = x; i <= mx; i += x)if(tub[i]){
			int j = i ^ s; if(j % x || !tub[j])continue;
			if(sum > 2)res = (res - 6ll * tub[i] * tub[j] % mod * (sum - 2)) % mod;
			res = (res - 4ll * tub[i] * tub[j]) % mod;
		}
		res = (res + mod) % mod; 
		res = 1ll * res * inv6 % mod * inv2 % mod * inv2 % mod;
		return res;
	}
}
int sol2(int x, int t){
	int res = 0;
	if(t == 3){
		for(int i = x; i <= mx; i += x)if(tub[i]){
			for(int j = i + x; j <= mx; j += x)if(tub[j]){
				int k = i ^ j ^ s;
				if(k % x || k <= j || !tub[k])continue;
				res = (res + 1ll * tub[i] * tub[j] % mod * tub[k]) % mod;
			}
		}
		if(s % x == 0 && tub[s]){
			int sum = 0;
			for(int i = x; i <= mx; i += x)if(i != s)add(sum, ch2(tub[i]));
			add(res, 1ll * sum * tub[s] % mod); 
			add(res, ch3(tub[s]));
		}
	}else{
		for(int i = x; i <= mx; i += x)if(tub[i]){
			for(int j = x; j <= mx; j += x)if(tub[j]){
				half[i ^ j] = (half[i ^ j] + 1ll * tub[i] * tub[j]) % mod;
			}
		}
		for(int i = x; i <= mx; i += x)if(tub[i]){
			for(int j = x; j <= mx; j += x)if(tub[j]){
				if(half[i ^ j ^ s])add(res, 1ll * tub[i] * tub[j] % mod * half[i ^ j ^ s] % mod);
			}
		}
		int sum = 0;  for(int i = x; i <= mx; i += x)sum += tub[i];
		for(int i = x; i <= mx; i += x)if(tub[i]){
			int j = i ^ s; if(j % x || !tub[j])continue;
			if(sum > 2)res = (res - 6ll * tub[i] * tub[j] % mod * (sum - 2)) % mod;
			res = (res - 4ll * tub[i] * tub[j]) % mod;
		}
		res = (res + mod) % mod; 
		res = 1ll * res * inv6 % mod * inv2 % mod * inv2 % mod;

		for(int i = x; i <= mx; i += x)if(tub[i]){
			for(int j = x; j <= mx; j += x)if(tub[j]){
				half[i ^ j] = 0;
			}
		}
	}
	return res;
}
int main(){
	freopen("choose.in","r",stdin);
	freopen("choose.out","w",stdout);
	n = read(), k = read(), s = read();
	for(int i = 1, x; i <= n; ++i){x = read(); mx = max(x, mx); ++tub[x];}
	if(k == 1){
		int ans = 1ll * tub[s] * s % mod;
		printf("%d\n",ans);
		return 0;
	}
	if(k == 2){
		int ans = 0;
		for(int i = 1; i <= mx; ++i)if((i ^ s) > i){
			ans = (ans + 1ll * tub[i] * tub[i ^ s] % mod * __gcd(i, i ^ s)) % mod;
		}
		printf("%d\n",ans);
		return 0;
	}
	deg = 1; while(deg <= mx)deg <<= 1;
	for(int i = 1; i <= mx; ++i){
		if(i <= B)f[i] = sol1(i, k);
		else f[i] = sol2(i, k);
	}
	int res = 0;
	for(int i = mx; i >= 1; --i){
		for(int j = i + i; j <= mx; j += i){
			f[i] = (f[i] - f[j]) % mod;
		}
		f[i] = (f[i] + mod) % mod;
		res = (res + 1ll * i * f[i]) % mod;
	}
	printf("%d\n",res);
	return 0;
}

C. 字符串

6

code

2023冲刺清北营3

疫情防控

image

code

集合操作

木得题解,暴力跑路

code

最短路

原题,但是原做法假了

考虑从n到1的路径,一定是先往回走一段,再沿着1到n的路径走一段,再往回走一段,再沿着1到n的路径走一段…,
把每段往回的路径的起点称为a类点,终点b类点,发现a、b两类点在1到n的路径上肯定是交错出现的,否则肯定不优。
设fi,j表示最后的特殊点为j,倒数第二个为i,且i为b类,j为a类;gi,j表示i和j均为a类。
转移时fi,j枚举下一个a类点,gi,j枚举i,j之间的b类点,用dijkstra转移即可。
时间复杂度O(n^3logn),常数较小。

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 = 255, inf = 0x1f1f1f1f;
int n, m, p[maxn], mp[maxn][maxn], f[maxn][maxn], g[maxn][maxn];
bool vis[maxn][maxn][2];
struct node{
	int dis, x, y, type;
	friend bool operator < (const node &x, const node &y){return x.dis > y.dis;}
};
priority_queue<node>q;
int main(){
	freopen("short.in","r",stdin);
	freopen("short.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)p[i] = read();
	memset(mp, 0x1f, sizeof(mp));
	for(int i = 1, u, v; i <= m; ++i){
		u = read(), v = read();
		mp[u][v] = p[v];
	}
	for(int k = 1; k <= n; ++k)
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= n; ++j)
				mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
	memset(f, 0x1f, sizeof(f));
	memset(g, 0x1f, sizeof(g));
	for(int i = 2; i <= n; ++i){
		g[1][i] = mp[i][1];
		q.push({g[1][i], 1, i, 1});
	}	
	while(!q.empty()){
		int x = q.top().x, y = q.top().y, tp = q.top().type; q.pop();
		if(vis[x][y][tp])continue;
		vis[x][y][tp] = true;
		if(tp){
			for(int v = 1; v <= n; ++v){
				if(g[x][y] + mp[x][v] + (v != y) * mp[v][y] < f[v][y]){
					f[v][y] = g[x][y] + mp[x][v] + (v != y) * mp[v][y];
					q.push({f[v][y], v, y, 0});
				}
			}
		}else{
			for(int v = 1; v <= n; ++v){
				if(mp[v][x] - p[x] + f[x][y] < g[y][v]){
					g[y][v] = mp[v][x] - p[x] + f[x][y];
					q.push({g[y][v], y, v, 1});
				}
			}
		}
	}
	int ans = inf;
	for(int i = 1; i <= n; ++i)ans = min(ans, g[i][n] + mp[i][n]);
	printf("%d\n",ans >= inf ? -1 : ans);
	return 0;
}

2023冲刺清北营4

猴戏世家

CERC2017B

直接做不好搞,考虑倒着来

那么每次删去一个栅栏,其影响是原先属于他的点变成属于他外侧的栅栏

称直接包含某个栅栏的栅栏为该栅栏的父亲,那么这个关系是森林的形态

从上到下扫描线,求出每个点属于哪个栅栏,每个栅栏的父亲是谁

然后倒着删去,用并查集维护答案即可

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 = 6e5 + 55;
struct node{
	int x, y, t;
	friend bool operator < (const node &x, const node &y){
		return x.y == y.y ? x.t > y.t : x.y > y.y;
	}
}p[maxn];
int n, m, ans[maxn], fa[maxn];
struct DSU{
	int f[maxn], val[maxn];
	void init(int mx){for(int i = 1; i <= mx; ++i)f[i] = i;}
	int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
	int query(int x){return val[fa(x)];}
	void merge(int x, int y){x = fa(x); y = fa(y); f[x] = y; val[y] += val[x];}
}S;
set<pii>s;
int main(){
	freopen("monkey.in","r",stdin);
	freopen("monkey.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i){p[i].x = read(); p[i].y = read(); p[i].t = 0;}
	m = read();
	for(int i = 1; i <= m; ++i){
		p[i + n].x = read(); p[i + n].y = read();
		p[i + n].t = i;
	}
	sort(p + 1, p + n + m + 1);
	for(int i = 1; i <= n + m; ++i)if(p[i].t){
        s.insert({p[i].x, p[i].t});
		auto it = s.find({p[i].x, p[i].t});
		if(++it != s.end())fa[p[i].t] = (*it).second;
		while(true){
			it = s.find({p[i].x, p[i].t});
			if(it == s.begin())break;
			--it;
			if((*it).second > p[i].t)s.erase(it);
			else break;
		}
	}else{
		auto it = s.lower_bound({p[i].x, p[i].t});
		if(it != s.end())++S.val[(*it).second];
	}
	S.init(m);
	for(int i = m; i >= 1; --i){
		ans[i] = S.query(i);
		if(fa[i])S.merge(i, fa[i]);
	}
	for(int i = 1; i <= m; ++i)printf("%d\n",ans[i]); printf("\n");
	return 0;
}

零糖麦片

CF468E

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 = 65, mx = 1e5 + 5, mod = 1e9 + 7;
int n, m, fac[mx], mp[maxn][maxn], ans;
struct node{int x, y, w;}d[maxn];
int la[maxn], pa, lb[maxn], pb, ca[maxn], cb[maxn];
unordered_map<ll, int>f[maxn], g[maxn];
int main(){
	freopen("cereal.in","r",stdin);
	freopen("cereal.out","w",stdout);
	n = read(), m = read();
	fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	for(int i = 1; i <= m; ++i){
		d[i].x = read(), d[i].y = read(), d[i].w = read();
		--d[i].w; if(d[i].w < 0)d[i].w += mod;
		la[++pa] = d[i].x; lb[++pb] = d[i].y;
	}
	sort(la + 1, la + pa + 1); pa = unique(la + 1, la + pa + 1) - la - 1;
	sort(lb + 1, lb + pb + 1); pb = unique(lb + 1, lb + pb + 1) - lb - 1;
	memset(mp, -1, sizeof(mp));
	for(int i = 1; i <= m; ++i){
		d[i].x = lower_bound(la + 1, la + pa + 1, d[i].x) - la;
		d[i].y = lower_bound(lb + 1, lb + pb + 1, d[i].y) - lb;
		mp[d[i].x][d[i].y] = d[i].w; ++ca[d[i].x]; ++cb[d[i].y];
	}
	f[0][0] += 1;
	while(true){
		int o = -1;
		for(int i = 1; i <= pb; ++i)if(cb[i] && (o == -1 || cb[o] > cb[i]))o = i;
		if(o == -1)break;
		for(int i = 1; i <= pa; ++i)if(mp[i][o] != -1 && ca[i]){
			ca[i] = 0;
			ll tmp = ((1ll << pb) - 1) << 1, mk = 0; vector<int>s;
			for(int j = 1; j <= pb; ++j)if(mp[i][j] != -1){
				s.push_back(j); cb[j] -= 1; mk |= 1ll << j;
				if(!cb[j])tmp ^= (1ll << j);
			}
			for(int j = 0; j <= m; ++j)for(auto x : f[j]){
				g[j][x.first & tmp] = (g[j][x.first & tmp] + x.second) % mod;
				for(int k : s)if(!(x.first >> k & 1))
					(g[j + 1][(x.first | (1ll << k)) & tmp] += (1ll * x.second * mp[i][k] % mod)) %= mod; 
			}
			for(int j = 0; j <= m; ++j)swap(f[j], g[j]), g[j].clear();
		}
	}
	for(int i = 0; i <= m; ++i)for(auto x : f[i])ans = (ans + 1ll * x.second * fac[n - i]) % mod;
	printf("%d\n",ans);
	return 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 mod = 1e9 + 7, maxn = 1e5 + 55;
struct data{
	int val, cnt;
	friend data operator + (const data &x, const data &y){
		if(x.val < y.val)return x;
		if(x.val > y.val)return y;
		return {x.val, (x.cnt + y.cnt) % mod};
	}
};
struct seg{
	struct node{
		int tag; data val;
	}t[maxn << 2 | 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.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_val(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_val(x << 1, l, mid, L, R, val);
		if(R > mid)modify_val(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	void modify_cnt(int x, int l, int r, int pos, int val){
		if(l == r){t[x].val.cnt = val; return;}
		push_down(x); int mid = (l + r) >> 1;
		if(pos <= mid)modify_cnt(x << 1, l, mid, pos, val);
		else modify_cnt(x << 1 | 1, mid + 1, r, pos, 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;
		push_down(x); int mid = (l + r) >> 1;
		if(R <= mid)return query(x << 1, l, mid, L, R);
		if(L > mid)return query(x << 1 | 1, mid + 1, r, L, R);
		return query(x << 1, l, mid, L, R) + query(x << 1 | 1, mid + 1, r, L, R);
	}
}T;
int st1[maxn], top1, st2[maxn], top2;
int n, p[maxn], f[maxn];
int main(){
	freopen("flower.in","r",stdin);
	freopen("flower.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)p[i] = read();
	f[0] = 1;
	for(int r = 1; r <= n; ++r){
		T.modify_cnt(1, 1, n, r, f[r - 1]);
		T.modify_val(1, 1, n, r, r, r);
		while(top1 && p[st1[top1]] > p[r]){
			T.modify_val(1, 1, n, st1[top1 - 1] + 1, st1[top1], p[st1[top1]] - p[r]);
			--top1;
		}
		st1[++top1] = r;
		while(top2 && p[st2[top2]] < p[r]){
			T.modify_val(1, 1, n, st2[top2 - 1] + 1, st2[top2], - p[st2[top2]] + p[r]);
			--top2;
		}
		st2[++top2] = r;
		data res = T.query(1, 1, n, 1, r);
		assert(res.val == r);
		f[r] = res.cnt;
	}
	printf("%d\n",f[n]);
	return 0;
}

2023冲刺清北营5

A. 吃粮

Ex=Eson+Efadegx+ax

Eleaf=aleaf

写成

Ex=kEfa+b

的形式向上推

根节点无父亲,可解

修改考虑每个 a 的贡献,就是乘上到根路径上的 k 和当前点 deg

预处理前缀积即可

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, 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];
vector<int>g[maxn];
int fa[maxn], k[maxn], b[maxn], ans;
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
void dfs(int x){
	if(g[x].size() == 1){k[x] = 0; b[x] = a[x]; return;}
	k[x] = b[x] = 0;
	for(int v : g[x])if(v != fa[x]){
		fa[v] = x; dfs(v);
		add(k[x], k[v]); add(b[x], b[v]);
	}
	k[x] = qpow((g[x].size() - k[x] + mod) % mod, mod - 2);
	b[x] = (1ll * a[x] * g[x].size() + b[x]) % mod * k[x] % mod;
}
void dfs1(int x){
	for(int v : g[x])if(v != fa[x]){
		if(g[v].size() == 1)k[v] = k[x]; 
		else k[v] = 1ll * k[v] * k[x] % mod;
		dfs1(v);
	}
}
void modify(int x, int val){
	val = 1ll * val * g[x].size() % mod * k[x] % mod;
	add(ans, val);
}
int main(){
	freopen("satisfy.in","r",stdin);
	freopen("satisfy.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	dfs(1); dfs1(1);
	printf("%d\n",ans = b[1]);
	int q = read();
	for(int i = 1; i <= q; ++i){
		int x = read(), y = read();
		modify(x, (y - a[x] + mod) % mod);
		a[x] = y; printf("%d\n",ans);
	}
	return 0;
}

B. 平衡

第一问好处理,关键在于第二问

考虑绝对值函数是个下凸函数,多个绝对值函数叠加仍然是下凸函数

对其求导,导数值单调不降

考虑二分答案 mid, 看哪些点在最优情况下取值可以 >=mid

使用网络流求解,每个点的点权是在 mid 处的导数(由于只关心 O(n) 个整数值,所以可以用差分)

对于限制 u<=v 连边 u>v 求最小权闭合子图即可

为啥是对的

考虑最小权闭合子图 S, 从中删去一些点 T, 那么 ST 为闭合子图

f(S)<=f(ST) 那么 f(T)<=0

由于导数单调,所以 T 取到 mid 一定不劣

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 ll inf = 1e15;
const int maxn = 505;
const int maxm = 2e5 + 55;

int n, nb, m1, m2, a[maxn], b[maxn], f[maxn], p[maxn];
ll g[maxn][maxn], ans;
int fa(int x){return f[x] == x ? x : fa(f[x]);}
bool mp[maxn][maxn];
struct Dinic{
	int s, t;
	int head[maxn], tot = 1;
	struct edge{int to, net; ll val;}e[maxm];
	void add(int u, int v, ll w){
		e[++tot].net = head[u];
		head[u] = tot;
		e[tot].to = v;
		e[tot].val = w;
	}
	void link(int u, int v, ll w){
		add(u, v, w); add(v, u, 0);
	}
	int dep[maxn], now[maxn];
	bool bfs(){
		for(int i = 1; i <= t; ++i)dep[i] = 0;
		queue<int>q; dep[s] = 1; q.push(s);
		now[s] = head[s];
		while(!q.empty()){
			int x = q.front(); q.pop();
			for(int i = head[x]; i; i = e[i].net){
				int v = e[i].to;
				if(e[i].val > 0 && dep[v] == 0){
					dep[v] = dep[x] + 1;
					now[v] = head[v];
					if(v == t)return true;
					q.push(v);
				}
			}
		}
		return false;
	}
	ll dfs(int x, ll from){
		if(x == t || from <= 0)return from;
		ll res = from; ;
		for(int &i = now[x]; i; i = e[i].net){
			int v = e[i].to;
			if(e[i].val > 0 && dep[v] == dep[x] + 1){
				ll k = dfs(v, min(res, e[i].val));
				if(k <= 0)dep[v] = 0;
				e[i].val -= k;
				e[i ^ 1].val += k;
				res -= k;
				if(res <= 0)break;
			}
		}
		return from - res;
	}
	ll dinic(){
		ll ans = 0;
		while(bfs())ans += dfs(s, inf);
		return ans;
	}
	void clear(){
		for(int i = 1; i <= t; ++i)head[i] = 0;
		s = t = 0; tot = 1;
	}
	void solve(int l, int r, int L, int R){
		if(l > r)return;
		if(L == R){
			for(int i = l; i <= r; ++i)ans += g[p[i]][L];
			return;
		}
		int mid = ((L + R) >> 1) + 1;
		s = r - l + 1 + 1, t = s + 1;
		for(int i = l; i <= r; ++i){
			int val = g[p[i]][mid - 1] - g[p[i]][mid];
			if(val > 0)link(s, i - l + 1, val);
			if(val < 0)link(i - l + 1, t, -val);
		}
		for(int i = l; i <= r; ++i)
			for(int j = l; j <= r; ++j)
				if(mp[p[i]][p[j]])link(i - l + 1, j - l + 1, inf);
		dinic();
		clear();
		vector<int>rp, lp;
		for(int i = 1; i <= r - l + 1; ++i)
			if(dep[i])rp.push_back(p[i + l - 1]);
			else lp.push_back(p[i + l - 1]);
		int md = l - 1; while(lp.size())p[++md] = lp.back(), lp.pop_back();
		int pr = md; while(rp.size())p[++pr] = rp.back(), rp.pop_back();
		solve(l, md, L, mid - 1);
		solve(md + 1, r, mid, R);
	}
}w;



int main(){
	freopen("balance.in","r",stdin);
	freopen("balance.out","w",stdout);
	n = read(), m1 = read(), m2 = read();
	for(int i = 1; i <= n; ++i)a[i] = b[i] = read();
	sort(b + 1, b + n + 1);  nb = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= n; ++i)
		for(int j = 0; j <= nb; ++j)
			g[i][j] = abs(b[j] - a[i]);
	for(int i = 1; i <= n; ++i)f[i] = i;
	for(int i = 1; i <= m1; ++i){
		int u = read(), v = read();
		u = fa(u); v = fa(v);
		if(u != v){
			for(int j = 0; j <= nb; ++j)g[u][j] += g[v][j], g[v][j] = 0;
			f[v] = u;
		}
	}
	for(int i = 1; i <= m2; ++i){
		int u = fa(read()), v = fa(read());
		if(u != v)mp[u][v] = true;
	}
	int r = 0;
	for(int i = 1; i <= n; ++i)if(fa(i) == i)p[++r] = i;
	w.solve(1, r, 1, nb);
	printf("%lld\n",ans);
	return 0;
}

C. 枸杞

题意比较生草,是每次在 [l,r] 中每张图走一步或者不走,不能同时不走。

先考虑走了 k

考虑邻接矩阵 A, 不考虑不能同时不走可以得到 (A+I)k 对角线求和

[l,r] 的答案乘起来得到答案 fk

考虑容斥掉不合法的,枚举有多少位置合法,用二项式反演可以得到答案

i=0k(ki)(1)kifi

对前缀求和即为最终答案。

考虑具体如何实现,首先对每个矩阵的处理需要先预处理光速幂,每次求两个矩阵乘积的对角线之和可以做到 n2

二项式反演的式子可以用 ntt 优化

然后大力卡常即可。

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

using namespace std;

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

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int mod = 998244353, maxn = 4e4 + 5;
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;
}
struct matrix{
	ull a[30][30];
	matrix(){memset(a, 0, sizeof(a));}
	friend matrix operator * (const matrix &x, const matrix &y){
		matrix res;
		for(int i = 0; i < 30; ++i)
			for(int j = 0; j < 30; ++j){
				for(int k = 0; k < 15; ++k)
					res.a[i][j] += x.a[i][k] * y.a[k][j];
				res.a[i][j] %= mod;
				for(int k = 15; k < 30; ++k)
					res.a[i][j] += x.a[i][k] * y.a[k][j];
				res.a[i][j] %= mod;
			}
		return res;
	}
	void clear(){memset(a, 0, sizeof(a));}
}tmp, b[105], bp[105];

int query(int k){
	int x = k % 100, y = k / 100;
	__int128_t ans = 0;
	for(int i = 0; i < 30; ++i)
		for(int j = 0; j < 30; ++j)
			ans += 1ll * b[x].a[i][j] * bp[y].a[j][i];
	return ans % mod;
}
int fac[maxn], ifac[maxn];
int rev[maxn], w[maxn], deg = 32768;
void init(){
	w[0] = 1; w[1] = qpow(3, (mod - 1) / deg);
	for(int i = 1; i < deg; ++i){
		rev[i] =  rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= deg >> 1;
		w[i] = 1ll * w[i - 1] * w[1] % mod;
	}
}
void ntt(ull a[]){
	for(int i = 1; i < deg; ++i)if(i < rev[i]) swap(a[i], a[rev[i]]);
	ull x, y;
	for(int l = 2, hl = 1; l <= deg; hl = l, l <<= 1){
		for(int i = 0; i < deg; i += l){
			for(int j = i; j < i + hl; ++j){
				x = a[j], y = 1ll * w[deg / l * (j - i)] * a[j + hl] % mod;
				a[j] = x + y; a[j + hl] = x - y + mod;
			}
		}
	}
	for(int i = 0; i < deg; ++i)a[i] %= mod;
}
void intt(ull a[]){
	ntt(a); reverse(a + 1, a + deg); int inv = qpow(deg, mod - 2);
	for(int i = 0; i < deg; ++i)a[i] = 1ll * a[i] * inv % mod;
}
int T, g[21][maxn];
ull f[maxn], h[21][21][maxn];
int main(){
	freopen("cholferry.in","r",stdin);
	freopen("cholferry.out","w",stdout);
	T = read();
	fac[0] = ifac[0] = 1; for(int i = 1; i <= 10000; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[10000] = qpow(fac[10000], mod - 2); for(int i = 9999; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	for(int i = 0; i < T; ++i){
		int n = read(), m = read();
		tmp.clear(); b[0].clear(); bp[0].clear();
		for(int j = 0; j < n; ++j)tmp.a[j][j] = 1;
		for(int j = 0; j < m; ++j){
			int u = read() - 1, v = read() - 1;
			tmp.a[u][v] = tmp.a[v][u] = 1;
		}
		for(int j = 0; j < n; ++j)b[0].a[j][j] = 1;
		for(int j = 0; j < n; ++j)bp[0].a[j][j] = 1;
		for(int j = 1; j <= 100; ++j)b[j] = b[j - 1] * tmp;
		for(int j = 1; j <= 100; ++j)bp[j] = bp[j - 1] * b[100];
		for(int j = 0; j <= 10000; ++j)g[i][j] = query(j);
	}
	for(int k = 0; k <= 10000; ++k){
		for(int l = 0; l < T; ++l){
			int mul = 1;
			for(int r = l; r < T; ++r){
				mul = 1ll * mul * g[r][k] % mod;
				h[l][r][k] = 1ll * mul * ifac[k] % mod;
			}
		}
		f[k] = 1ll * ((k & 1) ? mod - 1 : 1) * ifac[k] % mod;
	}
	init(); ntt(f);
	for(int l = 0; l < T; ++l){
		for(int r = l; r < T; ++r){
			ntt(h[l][r]);
			for(int i = 0; i < deg; ++i)h[l][r][i] = 1ll * h[l][r][i] * f[i] % mod;
			intt(h[l][r]); h[l][r][0] = 0;
			for(int i = 1; i <= 10000; ++i)h[l][r][i] = (h[l][r][i - 1] + 1ll * h[l][r][i] * fac[i]) % mod;
		}
	}

	int q = read();
	for(int i = 1; i <= q; ++i){
		int l = read() - 1, r = read() - 1, k = read();
		printf("%d\n",h[l][r][k]);
	}
	return 0;
}

2023冲刺清北营6

A. 万家灯火

暴力就是看范围内的他自己亮,他父亲灭的点的个数

考虑如果 x 固定,那么可以用树状数组在深度上维护进行优化

那么正解就是在点分树上做这个东西

对每一层点分,记录每个点的父亲,儿子个数,儿子中 1 的个数,深度

考虑查询时,需要向上跳,查询在某个深度以内符合要求的点,减去跳上来的那棵子树防止算重

修改时考虑他会影响到他自己,他儿子,他父亲,然后用上面的信息维护一下

虽然但是,好麻烦,不想写。。。

code

B. 逃亡

gi 为走 n 步后到达 i 的概率

gi=[inmod2]12n(nn+i2)

fi 为经过 i 的概率

对于经过 i,最后不在 i 的,其 >i<i 的部分是对称的,所以

fi=gi+2j>igj

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 = 1.2e7 + 55, mod = 998244353, inv2 = (mod + 1) >> 1;
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, mx, x[25];
int E[maxn], fac[maxn], ifac[maxn];
int c(int n, int m){return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int main(){
	freopen("exodus.in","r",stdin);
	freopen("exodus.out","w",stdout);
	n = read(), m = read();
	fac[0] = ifac[0] = 1;
	for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[n] = qpow(fac[n], mod - 2);
	for(int i = n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	int inv = qpow(inv2, n);
	for(int i = (n & 1) ? 1 : 2; i <= n; i += 2)E[i] = 1ll * inv * c(n, (n + i) / 2) % mod;
	int sum = 0;
	for(int i = n; i >= 1; --i){
		sum = (sum + E[i]) % mod;
		E[i] = (2ll * sum - E[i] + mod) % mod;
	}
	E[0] = 1;
	for(int i = 1; i <= m; ++i)x[i] = read();
	sort(x + 1, x + m + 1);
	for(int i = m; i >= 2; --i)x[i] -= x[i - 1];
	x[1] = n;
	for(int i = 2; i <= m; ++i)x[i] = min(x[i], n + n + 1);
	for(int i = 1; i <= m; ++i)x[i] += x[i - 1];
	mx = x[m] + n;

	int ans = 0;
	for(int i = 0; i <= mx; ++i){
		int res = 1;
		for(int j = 1; j <= m; ++j)if(abs(x[j] - i) <= n){
			res = 1ll * res * (mod + 1 - E[abs(x[j] - i)]) % mod;
		}
		ans = (ans + mod + 1 - res) % mod;
	}
	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 inf = 0x3f3f3f3f, maxn = 4005, maxm = maxn * maxn;

int cnt, s, t, head[maxn], tot = 1;
struct edge{int to, net, val;}e[maxm];
void add(int u, int v, int w){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
	e[tot].val = w;
}
void link(int u, int v, int w){
	add(u, v, w); add(v, u, 0);
}
int dep[maxn], now[maxn];
bool bfs(){
	for(int i = 1; i <= cnt; ++i)dep[i] = 0;
	queue<int>q; dep[s] = 1; q.push(s);
	now[s] = head[s];
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(e[i].val > 0 && dep[v] == 0){
				dep[v] = dep[x] + 1;
				now[v] = head[v];
				if(v == t)return true;
				q.push(v);
			}
		}
	}
	return false;
}
int dfs(int x, int from){
	if(x == t || from <= 0)return from;
	int res = from; int i;
	for(i = now[x]; i; i = e[i].net){
		int v = e[i].to;
		if(e[i].val > 0 && dep[v] == dep[x] + 1){
			int k = dfs(v, min(res, e[i].val));
			if(k <= 0)dep[v] = 0;
			e[i].val -= k;
			e[i ^ 1].val += k;
			res -= k;
			if(res <= 0)break;
		}
	}
	now[x] = i;
	return from - res;
}
int dinic(){
	int ans = 0;
	while(bfs())ans += dfs(s, inf);
	return ans;
}

int n, m, k, mp[maxn][maxn], rem[maxn], val[maxn], lz[maxn], lf[maxn];
bool vis[maxn];

int main(){
	freopen("underground.in","r",stdin);
	freopen("underground.out","w",stdout);
	n = read(), m = read(), k = read();
	for(int i = 1; i <= n; ++i)
		for(int j = i + 1; j <= n; ++j)
			mp[i][j] = inf;
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		mp[u][v] = min(mp[u][v], read());
	}
	t = ++cnt; val[t] = inf; ++cnt; link(cnt, t, inf);
	for(int i = 1; i <= n; ++i)rem[i] = t;
	for(int len = 2; len <= n; ++len){
		for(int l = 1, r = len; r <= n; ++l, ++r)if(mp[l][r] != inf){
			val[++cnt] = mp[l][r]; 
			for(int i = l; i < r; ++i){
				if(!vis[rem[i]]){
					link(cnt, rem[i], val[rem[i]]);
					link(rem[i] + 1, cnt + 1, inf);
					vis[rem[i]] = true;
				}
			}
			for(int i = l + 1; i < r; ++i)if(!lz[i])lz[i] = cnt;
			for(int i = l; i < r; ++i)vis[rem[i]] = false;
			for(int i = l; i < r; ++i)rem[i] = cnt;
			++cnt; link(cnt, cnt - 1, inf);
			for(int i = l + 1; i < r; ++i)if(!lf[i])lf[i] = cnt;
		}
	}
	s = ++cnt;
	for(int i = 1; i < n; ++i)if(!vis[rem[i]]){
		vis[rem[i]] = true; 
		link(s, rem[i], val[rem[i]]);
		link(rem[i] + 1, s + 1, inf);
	}
	for(int i = 1; i <= n; ++i)if(!lz[i])lz[i] = cnt;
	++cnt; link(cnt, s, inf);
	for(int i = 1; i <= n; ++i)if(!lf[i])lf[i] = cnt;

	for(int i = 1; i <= k; ++i){
		int u = read(), v = read();
		add(lz[u], lf[v], inf);
		add(lz[v], lf[u], inf);
	}
	printf("%d\n",dinic());
	return 0;
}

2023冲刺清北营7

A. a

发现区间合法的充要条件是

  • 总和为偶数
  • 相邻两个奇数之间没有 0

套路离线下来扫描线,线段树维护处理第一条限制

线段树需要做到翻转区间每个位置的奇偶性,对区间偶数位置 +1,求区间和

第二条限制记录上一个 0 的位置,到上个 0 的和为奇数/偶数,以及区间加法的左界

考虑如果新加入一个 0 ,两个 0 之间和如果为奇数,那么之前的 0 之前的位置都不能再选择为左端点,将左界右移即可

每次加法,考虑到上个 0 合为奇数,那么不能跨过上个 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 = 5e5 + 55;

int n, Q, a[maxn];
ll ans[maxn];
vector<pii>q[maxn];

struct seg{
	struct node{
		ll val;
		int rev, cnt0, cnt1, add0, add1;
	}t[maxn << 2 | 1];
	void rev(int x){
		t[x].rev ^= 1; swap(t[x].cnt0, t[x].cnt1);
	}
	void add(int x){
		if(t[x].rev)++t[x].add1;
		else ++t[x].add0;
		t[x].val += t[x].cnt0;
	}
	void push_down(int x){
		int ls = x << 1, rs = x << 1 | 1;
		t[ls].val += 1ll * t[x].add1 * t[ls].cnt1 + 1ll * t[x].add0 * t[ls].cnt0;
		t[rs].val += 1ll * t[x].add1 * t[rs].cnt1 + 1ll * t[x].add0 * t[rs].cnt0;
		if(t[ls].rev){t[ls].add0 += t[x].add1; t[ls].add1 += t[x].add0;}
		else  {t[ls].add0 += t[x].add0; t[ls].add1 += t[x].add1;}
		if(t[rs].rev){t[rs].add0 += t[x].add1; t[rs].add1 += t[x].add0;}
		else  {t[rs].add0 += t[x].add0; t[rs].add1 += t[x].add1;}
		if(t[x].rev)rev(ls), rev(rs);
		t[x].add0 = t[x].add1 = t[x].rev = 0;
	}
	void push_up(int x){
		t[x].cnt0 = t[x << 1].cnt0 + t[x << 1 | 1].cnt0;
		t[x].cnt1 = t[x << 1].cnt1 + t[x << 1 | 1].cnt1;
		t[x].val = t[x << 1].val + t[x << 1 | 1].val;
	}
	void build(int x, int l, int r){
		t[x].cnt0 = r - l + 1;
		if(l == r)return;
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
	}
	void modify_rev(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return rev(x);
		push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)modify_rev(x << 1, l, mid, L, R);
		if(R > mid)modify_rev(x << 1 | 1, mid + 1, r, L, R);
		push_up(x);
	}
	void add(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return add(x);
		push_down(x); int mid = (l + r) >> 1;
		if(L <= mid)add(x << 1, l, mid, L, R);
		if(R > mid)add(x << 1 | 1, mid + 1, r, L, R);
		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 = 0;
		if(L <= mid)ans +=  query(x << 1, l, mid, L, R);
		if(R > mid)ans +=  query(x << 1 | 1, mid + 1, r, L, R);
		return ans;
	}
}T;

int main(){
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	n = read(), Q = read();
	for(int i = 1; i <= Q; ++i)a[i] = read();
	for(int i = 1; i <= Q; ++i){
		int l = read(), r = read();
		q[r].push_back(pii(l, i));
	}
	T.build(1, 1, n);
	int thel = 1, las0 = 0, sum = 0;
	for(int r = 1; r <= n; ++r){
		if(a[r] & 1){
			T.modify_rev(1, 1, n, 1, r);
			sum ^= 1;
		}
		if(a[r] == 0){
			if(las0 && sum)thel = las0;
			las0 = r; sum = 0;
		}
		if(!las0 || !sum)T.add(1, 1, n, thel, r);
		else T.add(1, 1, n, las0, r);
		for(pii v : q[r])ans[v.second] = T.query(1, 1, n, v.first, r);
	}
	for(int i = 1; i <= Q; ++i)printf("%lld\n",ans[i]);
	return 0;
}

B. b

对询问按照 l1 排序,然后划分成若干 r1 降序的序列

考虑这样区间只有包含关系

因为 l1,r1 随机,那么期望下会划分成 q 个序列

每次用吉司机线段树维护操作

复杂度就是 mqlogn

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 = 15003, maxq = 100005;


int n, m, Q, a[maxn], b[maxn];
struct seg{
	struct node{
		int mx, cnt, cmx, tag;
		ll sum;
	}t[maxn << 2 | 1];
	void push_up(int x){
		int ls = x << 1, rs = x << 1 | 1;
		t[x].sum = t[ls].sum + t[rs].sum;
		t[x].mx = max(t[ls].mx, t[rs].mx);
		t[x].cmx = max(t[ls].cmx, t[rs].cmx);
		t[x].cnt = 0;
		if(t[ls].mx == t[x].mx)t[x].cnt += t[ls].cnt;
		else t[x].cmx = max(t[x].cmx, t[ls].mx);
		if(t[rs].mx == t[x].mx)t[x].cnt += t[rs].cnt;
		else t[x].cmx = max(t[x].cmx, t[rs].mx);		
	}
	void upd(int x, int val){
		if(t[x].mx <= val)return;
		t[x].sum = t[x].sum + 1ll * (val - t[x].mx) * t[x].cnt; 
		t[x].mx = 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 build(int x, int l, int r){
		t[x].tag = 0;
		if(l == r){
			t[x].mx = a[l];
			t[x].cnt = 1;
			t[x].cmx = -1;
			t[x].sum = a[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){
		if(t[x].mx <= val)return;
		if(L <= l && r <= R){
			if(t[x].cmx < val)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].sum;
		push_down(x); int mid = (l + r) >> 1; ll ans = 0;
		if(L <= mid)ans += query(x << 1, l, mid, L, R);
		if(R > mid)ans += query(x << 1 | 1, mid + 1, r, L, R);
		return ans;
	}
}T;
struct node{int l, r, x;}d[maxn];
struct query{
	int l1, r1, l2, r2, id;
	void in(int x){l1 = read(), r1 = read(), l2 = read(), r2 = read(), id = x;}
	friend bool operator < (const query &x, const query &y){
		return x.l1 == y.l1 ? x.r1 < y.r1 : x.l1 < y.l1;
	}
}q[maxq];
vector<query>vec[maxq];
ll ans[maxq];
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n = read(), m = read(), Q = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= m; ++i)d[i].l = read(), d[i].r = read(), d[i].x = read();
	for(int i = 1; i <= Q; ++i)q[i].in(i);
	sort(q + 1, q + Q + 1);
	for(int i = 1; i <= Q; ++i){
		for(int j = 1; j <= Q; ++j)if(vec[j].empty() || vec[j].back().r1 >= q[i].r1){
			vec[j].push_back(q[i]); break;
		}
	}
	int all = 0;
	for(int i = 1; i <= Q; ++i){
		if(vec[i].empty())break;
		T.build(1, 1, n);
		query &o = vec[i].back();
		for(int j = o.l1; j <= o.r1; ++j)T.modify(1, 1, n, d[j].l, d[j].r, d[j].x);
		ans[o.id] = T.query(1, 1, n, o.l2, o.r2);	
		int l = o.l1, r = o.r1; ++all;
		vec[i].pop_back();
		while(vec[i].size()){
			++all;
			o = vec[i].back();
			while(l > o.l1)--l, T.modify(1, 1, n, d[l].l, d[l].r, d[l].x);
			while(r < o.r1)++r, T.modify(1, 1, n, d[r].l, d[r].r, d[r].x);
			ans[o.id] = T.query(1, 1, n, o.l2, o.r2);
			vec[i].pop_back();
		}
	}
	
	for(int i = 1; i <= Q; ++i)printf("%lld\n",ans[i]);
	return 0;
}

C. c

不考虑选 m 个,那么异或为 i 的用集合幂级数表示为 [xi](1+xai) (可以理解为对指数进行特殊的运算?比如说这里显然应该用异或)

限制选择 m 个,那么就再加入一个元 y

[xi](1+xaiy)

显然对 y 就是普通的乘法卷积

考虑把这玩意的 fwt 变换后的东西直接搞出来

先把 y 看成系数,那么对一项变换之后,对其每个位置都是 (1+y) 或者 (1y)

那么变换后 [xi]=[ym](1+x)a(1x)na

对他逆变换回去即可

考虑如何求 a

ai 的位置放 1 代表 y 的系数,对其做 fwt 即可知道在某位置是加还是减,那么可以把所有的放在一起做,得到 i 位置上的值为 ab

其中 a1+y 的个数, b1y 的个数,又有 a+b=n 可以解得 a,b

那么只剩下 求[ym](1+y)a(1y)na

为了方便下面用 x

[xm](1+x)a(1x)na

=i=0a(ai)(nami)(1)mi

=a!(na)!i=0a(1)mii!(mi)!1(ai)!(nm(ai))!

ntt 卷积即可

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 = (1 << 17) * 4 + 5, mod = 998244353, inv2 = (mod + 1) >> 1;
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 deg, w[maxn], rev[maxn];
void init(int len){
	deg = 1; while(deg <= len)deg <<= 1;
	w[1] = qpow(3, (mod - 1) / deg); w[0] = 1;
	for(int i = 1; i < deg; ++i){
		rev[i] = rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= (deg >> 1);
		w[i] = 1ll * w[i - 1] * w[1] % mod;
	}
}
void ntt(int f[]){
	for(int i = 1; i < deg; ++i)if(rev[i] > i)swap(f[i], f[rev[i]]);
	for(int l = 2, hl = 1; l <= deg; hl = l, l <<= 1)
		for(int i = 0; i < deg; i += l)
			for(int j = i; j < i + hl; ++j){
				int x = f[j], y = 1ll * w[deg / l * (j - i)] * f[j + hl] % mod;
				f[j] = (x + y) % mod, f[j + hl] = (x - y + mod) % mod;
			}
}
void intt(int f[]){
	ntt(f); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
	for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
}

int n, m, k, a[maxn], fac[maxn], ifac[maxn], f[maxn], g[maxn];
void fwt_xor(int f[], int op){
	for(int hl = 1, l = 2; l <= k; hl = l, l <<= 1)
		for(int i = 0; i < k; i += l)
			for(int j = i; j < i + hl; ++j){
				int x = f[j], y = f[j + hl];
				f[j] = 1ll * (x + y) * op % mod;
				f[j + hl] = 1ll * (x - y + mod) * op % mod;
			}
}
int main(){
	freopen("c.in", "r", stdin);
	freopen("c.out", "w", stdout);
	n = read(), m = read(), k = read();
	for(int i = 1; i <= n; ++i)++a[read()];
	fac[0] = ifac[0] = 1;
	for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[n] = qpow(fac[n], mod - 2);
	for(int i = n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	for(int i = 0; i <= m; ++i)
		if((m - i) & 1)f[i] = (mod - 1ll * ifac[i] * ifac[m - i] % mod) % mod;
		else f[i] = 1ll * ifac[i] * ifac[m - i] % mod;
	for(int i = 0; i <= n - m; ++i)g[i] = 1ll * ifac[i] * ifac[n - m - i] % mod;
	init(n + 1); ntt(f); ntt(g);
	for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
	intt(f);
	for(int i = 0; i <= n; ++i)f[i] = 1ll * f[i] * fac[i] % mod * fac[n - i] % mod;
	fwt_xor(a, 1);
	for(int i = 0; i < k; ++i)a[i] = f[1ll * (a[i] + n) * inv2 % mod];
	fwt_xor(a, inv2);
	for(int i = 0; i < k; ++i)printf("%d ",a[i]); printf("\n");
	return 0;
}

2023冲刺清北营8

A. 内鬼

两个内鬼分开考虑最后合并

考虑一个内鬼 fs,i 表示刀掉了 s 集合的人,目前在 i 号点的最小时刻

按照 s 分层转移,同层跑最短路转移

最后合并起来就好。

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 = 2e4 + 55, inf = 0x3f3f3f3f;
int n, m, k, se, tmax, x, y, head[maxn], tot;
vector<int>vec[maxn][9];
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 f[256][maxn]; bool vis[maxn];
priority_queue<pii, vector<pii>, greater<pii>>q;
void dij(int *dis){
	for(int i = 1; i <= n; ++i)vis[i] = false;
	for(int i = 1; i <= n; ++i)q.push(pii(dis[i], i));
	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(pii(dis[v], v));
			}
		}
	}
}
void solve(int S, int ans[]){
	memset(f, 0x3f, sizeof(f));
	for(int i = 0; i < (1 << k); ++i)ans[i] = inf;
	f[0][S] = 0;
	for(int s = 0; s < (1 << k); ++s){
		dij(f[s]);
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= k; ++j)if(!((s >> (j - 1)) & 1)){
				auto it = lower_bound(vec[i][j].begin(), vec[i][j].end(), f[s][i]);
				if(it != vec[i][j].end())f[s | (1 << (j - 1))][i] = min(f[s | (1 << (j - 1))][i], *it);
			}
		for(int i = 1; i <= n; ++i)ans[s] = min(ans[s], f[s][i]);
	}
}
int a[256], b[256];
int main(){
	freopen("ghost.in","r",stdin);
	freopen("ghost.out","w",stdout);
	n = read(), m = read(), k = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read(), w = read();
		add(u, v, w); add(v, u, w);
	}
	se = read(); tmax = read();
	for(int i = 1; i <= se; ++i){
		int p = read(), x = read(), t = read();
		vec[x][p].push_back(t);
	}
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= k; ++j)
			sort(vec[i][j].begin(), vec[i][j].end());
	x = read(), y = read();
	solve(x, a); solve(y, b);
	int ans = inf;
	for(int i = 0; i < (1 << k); ++i)ans = min(ans, max(a[i], b[((1 << k) - 1) ^ i]));
	printf("%d\n",ans == inf ? -1 : ans);	
	return 0;
}

B. 树上最长上升子序列

相当于求以每个点为结尾的最长下降子序列,翻转值域,还是最长上升子序列

序列上是维护每个长度结尾最小值,每次二分一个位置修改

放到树上那就枚举起点,仍然二分做递归时记录修改的位置,回溯撤销即可

那么现在是 n2log

考虑树上问题,而且与链相关,那么尝试点分治

那么现在需要合并两个数组,发现是直接对应位置取 min, 按照长剖的思想,这部分复杂度是对的

那么考虑对与某子树,需要其他子树信息合并再加上分治中心的信息一起向下做

于是维护前缀后缀 min 即可

但是为了保证复杂度,这里实现需要一点细节

一种可行做法是按照数组长度对数组排序,前缀 min 直接维护复杂度正确,从后往前维护后缀,只用一个数组

合并前后缀用较短的一段,记录下原值,然后对应修改,递归处理子树后再回溯即可

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

using namespace std;

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

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 2e5 + 55, inf = 0x3f3f3f3f;
int n, m, a[maxn], b[maxn], his[maxn];
vector<int>g[maxn];

int ans[maxn];
int mx[maxn], si[maxn], S, rt;
vector<int>vec[maxn], pre[maxn], suf, son;
bool del[maxn];

void find_rt(int x, int fa){
	si[x] = 1; mx[x] = 0;
	for(int v : g[x])if(v != fa && !del[v]){
		find_rt(v, x); si[x] += si[v];
		mx[x] = max(mx[x], si[v]);
	}
	mx[x] = max(mx[x], S - si[x]);
	if(mx[x] < mx[rt])rt = x;
}
bool cmp(int x, int y){return vec[x].size() < vec[y].size();}
void merge(vector<int>&x, vector<int>&y){
	if(x.size() < y.size())swap(x, y);
	for(int i = 0; i < y.size(); ++i)x[i] = min(x[i], y[i]);
	y.clear();
}
void dfs_up(int x, int fa){
	vec[x].clear();
	for(int v : g[x])if(v != fa && !del[v]){
		dfs_up(v, x);
		merge(vec[x], vec[v]);
	}
	auto it = lower_bound(vec[x].begin(), vec[x].end(), a[x]);
	if(it == vec[x].end())vec[x].push_back(a[x]);
	else *it = a[x];
}
void dfs_down(int x, int fa, int len){
	int k = lower_bound(b + 1, b + len + 1, a[x]) - b;
	int rem = b[k]; b[k] = a[x]; if(k == len)++len, b[len] = inf;
	ans[x] = max(ans[x], k);
	for(int v : g[x])if(v != fa && !del[v])dfs_down(v, x, len);
	b[k] = rem;
}
void ckmx(vector<int>&x, const vector<int>&y){
	while(x.size() < y.size())x.push_back(inf);
	for(int i = 0; i < y.size(); ++i)x[i] = min(x[i], y[i]);
}
void calc(int x){
	son.clear();
	for(int v : g[x])if(!del[v])dfs_up(v, x);
	sort(g[x].begin(), g[x].end(), cmp);
	for(int v : g[x])if(!del[v]){
		son.push_back(v);
		pre[son.size()] = vec[v]; 
		if(son.size())ckmx(pre[son.size()], pre[son.size() - 1]);
	}
	int k = lower_bound(pre[son.size()].begin(), pre[son.size()].end(), a[x]) - pre[son.size()].begin();
	ans[x] = max(ans[x], k + 1);
	int len = 0, remlen = 0, hislen = 0;
	while(son.size()){
		pre[son.size()].clear();
		remlen = min(len, (int)pre[son.size() - 1].size()); hislen = len;
		while(len < pre[son.size() - 1].size())b[++len] = inf;
		for(int i = 1; i <= remlen; ++i)his[i] = b[i];
		for(int i = 0; i < pre[son.size() - 1].size(); ++i)b[i + 1] = min(b[i + 1], pre[son.size() - 1][i]);
		b[++len] = inf;
		int k = lower_bound(b + 1, b + len + 1, a[x]) - b;
		int rem = b[k]; b[k] = a[x]; if(k == len)++len, b[len] = inf;
		dfs_down(son.back(), x, len);
		b[k] = rem;
		for(int i = 1; i <= remlen; ++i)b[i] = his[i];
		len = hislen;
		while(len < vec[son.back()].size())b[++len] = inf;
		for(int i = 0; i < vec[son.back()].size(); ++i){
			b[i + 1] = min(b[i + 1], vec[son.back()][i]);
		}
		vec[son.back()].clear();
		son.pop_back();
	}
}
void solve(int x){
	del[x] = true; calc(x); 
	for(int v : g[x])if(!del[v]){
		rt = 0; S = si[v]; find_rt(v, x);
		if(si[v] != S){
			S = si[v]; rt = 0; find_rt(v, x);
		}
		solve(rt);
	}
}
int main(){
	freopen("lot.in","r",stdin);
	freopen("lot.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = b[i] = read();
	sort(b + 1, b + n + 1); m = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= n; ++i)a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
	for(int i = 1; i <= n; ++i)a[i] = m - a[i] + 1;
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	mx[0] = S = n; find_rt(1, 0); solve(rt);
	for(int i = 1; i <= n; ++i)printf("%d\n",ans[i]);
	return 0;
}

C. 海盗

居然考了 slope trick 这种邪门东西

fi,x 表示前 i 个数已经合法,向后传递了 x 个金币的最小代价

枚举 n>1 传了多少个

用填表发现每次转移的是连续区间,所以可以单调队列优化。

那么这里就有不少分了。

进一步思考,把每次操作总结为

  • fi(x)=miny=xx+tfi1(y)

  • 平移函数

  • f(x)+=|x|

发现 f(x) 是个下凸函数

那么三个操作等价于

  • 把最小值右侧的函数向右平移

  • 平移

  • 加上一个下凸函数

发现 |x| 这玩意性质很好,于是用 slope trick

以最小值为 x=0 的位置,两侧维护对顶堆,记录两个函数平移量

对第三种操作按照拐点分类讨论即可

现在还有最后的问题就是 n>1 多少

这玩意是个单峰函数,于是直接三分即可

证明略(太菜了不会

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, a[maxn], l[maxn], r[maxn];
struct slope_trick{
	priority_queue<ll>ql;
	priority_queue<ll, vector<ll>, greater<ll>>qr;
	ll m1, m2, val;
	void init(ll x){
		m1 = m2 = val = 0;
		while(ql.size())ql.pop();
		while(qr.size())qr.pop();
		for(int i = 1; i <= n + 10; ++i)ql.push(x), qr.push(x);
	}
	void ckmi(ll t){m1 -= t;}
	void move(ll t){m1 += t; m2 += t;}
	void pushl(ll t){ql.push(t - m1);}
	void pushr(ll t){qr.push(t - m2);}
	ll topl(){return ql.top() + m1;}
	ll topr(){return qr.top() + m2;}
	void addabs(){
		if(topl() <= 0 && topr() >= 0)pushl(0), pushr(0);
		else if(topl() > 0){
			val += min(abs(topl()), abs(topr()));
			pushr(topl()); ql.pop();
			pushl(0); pushl(0);
		}else if(topr() < 0){
			val += min(abs(topl()), abs(topr()));
			pushl(topr()); qr.pop();
			pushr(0); pushr(0);
		}
	}
	ll query(ll t){
		if(t < topl()){
			ll s = 1;
			while(ql.size() && topl() > t){
				ll x = topl(); ql.pop();
				ll y = max(t, topl());
				val += (x - y) * s;
				++s;
			}
		}else if(t > topr()){
			ll s = 1;
			while(qr.size() && topr() < t){
				ll x = topr(); qr.pop();
				ll y = min(t, topr());
				val += (y - x) * s;
				++s;
			}
		}
		return val;
	}
}st;
ll calc(ll x){
	st.init(x);
	for(int i = 1; i <= n; ++i){
		st.ckmi(r[i] - l[i]);
		st.move(a[i] - l[i]);
		st.addabs();
	}
	return st.query(x);
}
int main(){
	freopen("pirate.in","r",stdin);
	freopen("pirate.out","w",stdout);
	n = read(); ll sum = 0;
	for(int i = 1; i <= n; ++i){
		a[i] = read(), l[i] = read(), r[i] = read();
		sum += a[i];
	}
	ll l = -sum, r = sum, ans = 2e18;
	while(r - l > 5){
		ll m1 = (l + r) >> 1, m2 = m1 + 1;
		ll v1 = calc(m1), v2 = calc(m2);
		if(v1 > v2)l = m1; else r = m2;
	}
	for(ll i = l; i <= r; ++i)ans = min(ans, calc(i));
	printf("%lld\n",ans);
	return 0;
}

2023冲刺清北营10

A. 九转大肠

~~ Delov 看见原味款的馋哭了 ~~

先从下往上推出总方案数,就是每个点儿子个数阶乘的积

然后考虑根据当前点的答案数组向下推,得到儿子们的答案数组

发现需要知道前面有多少子树以及子树大小之和

那么用 fi,j 表示选了 i 个子树,大小和为 j 的方案数

每次对于一个子树删去其对背包的贡献计算答案即可

我写的代码在计算答案时复杂度存在一点问题,开一个临时数组存一下最后再统一放到答案数组里复杂度大概就对了,但是懒得写了。

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

using namespace std;

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

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int 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;
}
int n, f[maxn], g[maxn], h[maxn], fac[maxn], ifac[maxn], a[maxn][maxn], si[maxn];
vector<int>son[maxn];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
void dfs(int x){
	g[x] = 1; si[x] = 1;
	for(int v : son[x]){
		dfs(v); si[x] += si[v];
		g[x] = 1ll * g[x] * g[v] % mod;
	}
	g[x] = 1ll * g[x] * fac[son[x].size()] % mod;
}
int dp[maxn][maxn], tmp[maxn][maxn];
bool cmp(int x, int y){return si[x] < si[y];}
void sol(int x){
	sort(son[x].begin(), son[x].end(), cmp);
	int s = son[x].size();
	for(int i = 1; i <= n; ++i)h[i] = 1ll * a[x][i] * ifac[s] % mod;
	for(int i = 0; i <= s; ++i)for(int j = 0; j <= si[x]; ++j)dp[i][j] = 0;
	dp[0][0] = 1; int sum = 0;
	for(int i = 0; i < s; ++i){
		for(int j = i; j >= 0; --j)
			for(int k = 0; k <= sum; ++k)if(dp[j][k])
				add(dp[j + 1][k + si[son[x][i]]], dp[j][k]);	
		sum += si[son[x][i]];
	}
	for(int i = 0; i < s; ++i){
		int v = son[x][i];
		if(i && si[v] == si[son[x][i - 1]]){
			for(int j = 1; j <= n; ++j)a[v][j] = a[son[x][i - 1]][j];
			continue;
		}
		for(int j = 0; j < s; ++j)
			for(int k = 0; k <= si[x]; ++k)tmp[j][k] = dp[j][k];
		for(int j = 0; j < s - 1; ++j)
			for(int k = 0; k <= si[x]; ++k)
				add(tmp[j + 1][k + si[v]], mod - tmp[j][k]);
		for(int j = 1; j <= n; ++j)if(h[j])
			for(int k = 0; k < s; ++k)
				for(int p = 0; p <= si[x]; ++p)if(tmp[k][p])
					a[v][j + p + 1] = (a[v][j + p + 1] + 1ll * h[j] * tmp[k][p] % mod * fac[k] % mod * fac[s - 1 - k]) % mod;
	}
	for(int v : son[x])sol(v);
}
int main(){
	freopen("intestine.in","r",stdin);
	freopen("intestine.out","w",stdout);
	n = read(); for(int i = 2; i <= n; ++i)son[f[i] = read()].push_back(i);
	fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i >= 0; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	dfs(1); a[1][1] = g[1]; sol(1);
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j)
			printf("%d ",a[i][j]);
		printf("\n");
	}
	return 0;
}

B. 原本味道

Delov 喜欢的味道

左端点不好算,但是右端点好算,那么如果能算出来左右端点的距离,那么就能求得答案

考虑如果左端点移动右端点不动为 1, 左端点不动,右端点动为 +1

那么答案就是从最后一个距离为 1 的位置开始往后的总和

那么其实这就是最大后缀和的位置

考虑到 1/1/0 的变化是有限段,那么对时间离散化,求得每个前缀的最大后缀,每次分几种情况讨论即可

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

using namespace std;

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

ll a1, b1, a2, b2, n, lcm, c1, c2;
const int maxn = 1e6 + 5;
struct point{
	ll r; int x;	
	friend bool operator < (const point &x, const point &y){return x.r < y.r;}
}val[maxn * 8];
ll suf[maxn * 8], pre[maxn * 8], sum, mxs, tot;
void solve(){
	ll q, bl, ans; cin >> q;
	bl = (q - 1) / lcm; q = (q - 1) % lcm + 1;
	int pos = upper_bound(val + 1, val + tot + 1, point{q, 0}) - val - 1;
	ans = (q - val[pos].r) * val[pos + 1].x + suf[pos];
	ans = max(ans, 0ll);
	if(bl){
		ans = max(ans, pre[pos] + (q - val[pos].r) * val[pos + 1].x + mxs);
		ans = max(ans, pre[pos] + (q - val[pos].r) * val[pos + 1].x + (bl - 1) * sum + mxs);
	}
	ll cnt = (bl * lcm + q - 1) / (a1 + b1) * a1;
	if((bl * lcm + q - 1) % (a1 + b1) + 1 <= a1)cnt += (bl * lcm + q - 1) % (a1 + b1) + 1;
	else cnt += a1;
	ans = cnt - ans;
	printf("%lld\n", ans);
}
int main(){
	freopen("snow.in","r",stdin);
	freopen("snow.out","w",stdout);
	cin >> a1 >> b1 >> a2 >> b2 >> n;
	lcm = (a1 + b1) / __gcd(a1 + b1, a2 + b2) * (a2 + b2);
	c1 = lcm / (a1 + b1); c2 = lcm / (a2 + b2);
    int p = 1, q = 1;
	while(p <= c1 + c1 && q <= c2 + c2){
		ll n1, n2;
		if(p & 1)n1 = 1ll * (p >> 1) * (a1 + b1) + a1;
		else n1 = 1ll * (p >> 1) * (a1 + b1);
		if(q & 1)n2 = 1ll * (q >> 1) * (a2 + b2) + a2;
		else n2 = 1ll * (q >> 1) * (a2 + b2);
		int x = (p & 1) - (q & 1);
		if(n1 < n2)val[++tot] = point{n1, x}, ++p;
		else if(n1 > n2)val[++tot] = point{n2, x}, ++q;
		else val[++tot] = point{n1, x}, ++p, ++q;
	}
	suf[0] = pre[0] = 0ll;
	for(int i = 1; i <= tot; ++i){
		suf[i] = suf[i - 1] + (val[i].r - val[i - 1].r) * val[i].x;
		suf[i] = max(suf[i], 0ll);
		pre[i] = pre[i - 1] + (val[i].r - val[i - 1].r) * val[i].x;
	}
  	sum = mxs = 0;
	for(int i = tot; i >= 1; --i){
		sum += (val[i].r - val[i - 1].r) * val[i].x;
		mxs = max(mxs, sum);
	}
	for(int i = 1; i <= n; ++i)solve();	
	return 0;
}

C. 顶级厨师

显然对于一种技能,如果某一天减为 0, 那么之前不选他一定更优

于是可以不考虑这个情况直接求解

fi,p,q,0/1/2 表示前 i 天,最后一次选择的是 0/1/2 技能,其他两个技能连续多少天没学的最大值

然后考虑某种技能,其两次选择之间一定不会大于 O(w) 天, 否则一定不优,于是缩小枚举范围即可通过

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, B = 200, inf = 0x3f3f3f3f;
int n, a[maxn][3], ans, f[maxn][B + 5][B + 5][3];
void ckmx(int &x, int y){if(x < y)x = y;}
void sol(){
	n = read();
	for(int i = 1; i <= n; ++i){
		a[i][0] = read();
		a[i][1] = read();
		a[i][2] = read();
	}
	ans = 0;
	for(int i = 0; i <= n; ++i)
		for(int j = 0; j <= B; ++j)
			for(int k = 0; k <= B; ++k)
				for(int s = 0; s < 3; ++s)
					f[i][j][k][s] = -inf;
	f[0][0][0][0] = f[0][0][0][1] = f[0][0][0][2] = 0;
	for(int i = 0; i <= n; ++i){
		for(int p = 0; p <= min(i, B); ++p)
			for(int q = 0; q <= min(i, B); ++q)
				for(int las = 0; las < 3; ++las){
					if(i == n)ans = max(ans, f[i][p][q][las]);
					else{
						ckmx(f[i + 1][p + (p != 0)][q + (q != 0)][las], f[i][p][q][las] + a[i + 1][las] - p - q);
						if(las == 0){
							ckmx(f[i + 1][2][q + (q != 0)][1], f[i][p][q][las] + a[i + 1][1] - 1 - q);
							ckmx(f[i + 1][2][p + (p != 0)][2], f[i][p][q][las] + a[i + 1][2] - 1 - p);
						}else if(las == 1){
							ckmx(f[i + 1][2][q + (q != 0)][0], f[i][p][q][las] + a[i + 1][0] - 1 - q);
							ckmx(f[i + 1][p + (p != 0)][2][2], f[i][p][q][las] + a[i + 1][2] - 1 - p);
						}else if(las == 2){
							ckmx(f[i + 1][q + (q != 0)][2][0], f[i][p][q][las] + a[i + 1][0] - 1 - q);
							ckmx(f[i + 1][p + (p != 0)][2][1], f[i][p][q][las] + a[i + 1][1] - 1 - p);
						}
					}
				}
	}
	printf("%d\n",ans);
}

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

2023冲刺清北营11

A. 一瓶消毒液

直接 n3 枚举,然后求两线段之间距离,判断第三条是否大于这个距离

两线段之间距离,要么是端点组合,要么是点到直线(如果垂足在线段上)

这题还卡精度,尽量不要用小数

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef __int128_t lll;
ll read(){
	ll 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 = 505;

struct point{
	lll x, y;
	void init(){x = read(); y = read();}
	friend point operator - (const point &x, const point &y){return {x.x - y.x, x.y - y.y};}
	friend lll operator * (const point &x, const point &y){return x.x * y.y - x.y * y.x;}
}a[maxn];
lll dis2(point &x, point &y){return (x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y);}
int n;
lll d[maxn][maxn], mi[maxn][maxn], len[maxn];

lll solve(int x, int y, int z){
    lll mx = max({d[x][y], d[x][z], d[y][z]});
    if(d[x][y] >= mx && d[x][y] > d[x][z] + d[y][z])return 1e38;
    if(d[x][z] >= mx && d[x][z] > d[x][y] + d[y][z])return 1e38;
    lll mul = (a[y] - a[x]) * (a[z] - a[x]);
    if(mul < 0)mul = -mul;
    return (lll)ceil(1.0 * mul * mul / d[y][z]);
}
lll calc(int i, int j){
    if((((a[j] - a[i]) * (a[i] - a[i + n]) > 0) ^ ((a[j + n] - a[i]) * (a[i] - a[i + n]) > 0)) &&
        (((a[i] - a[j]) * (a[j] - a[j + n]) > 0) ^ ((a[i + n] - a[j]) * (a[j] - a[j + n]) > 0)))
		return 0;
	return min({d[i][j], d[i][j + n], d[i + n][j], d[i + n][j + n], solve(i, j, j + n), solve(i + n, j, j + n), solve(j, i, i + n), solve(j + n, i, i + n)});
}
void solve(){
	n = read();
	int ans = 0;
	for(int i = 1; i <= n; ++i){
		ll x = read(), y = read(), p = read(), q = read();
		a[i] = {x, y}; a[i + n] = {p, q};
	}
	for(int i = 1; i <= n + n; ++i)
		for(int j = 1; j <= n + n; ++j)
			d[i][j] = dis2(a[i], a[j]);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			if(i != j)mi[i][j] = calc(i, j);
	for(int i = 1; i <= n; ++i)len[i] = d[i][i + n];
	for(int i = 1; i <= n; ++i)
		for(int j = i + 1; j <= n; ++j)
			for(int k = j + 1; k <= n; ++k)
				if(mi[i][j] <= len[k] || mi[i][k] <= len[j] || mi[j][k] <= len[i])++ans;
	printf("%d\n",ans);
}
int main(){
	freopen("dis.in","r",stdin);
	freopen("dis.out","w",stdout);
	int t = read(); for(int i = 1; i <= t; ++i)solve();
	return 0;
}

B. 一张桌子

f(x,y,z)f(z,y,x)=axby+aybz+azbxazbyaybxaxbz

=ax(bybz)+bx(azay)+aybzayby+aybyazby

=ax(bybz)+bx(azay)+ay(bzby)+by(ayaz)

=(ayax)(bybz)(bybx)(azay)

这玩意是叉积。

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 = 3005;
int n;
struct node{
	int x, y;
	void in(){x = read(), y = read();}
	friend node operator - (const node &x, const node &y){return {x.x - y.x, x.y - y.y};}
}d[maxn], las;
bool vis[maxn]; int ans[maxn];
bool check(node x, node y){
	ll cross = 1ll * x.x * y.y - 1ll * x.y * y.x;
	return (cross > 0 || (cross == 0 && abs(x.x) < abs(y.x)));
}
int main(){
	freopen("table.in","r",stdin);
	freopen("table.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)d[i].in();
	for(int i = 1; i <= n; ++i){
		int res = 0;
		for(int j = 1; j <= n; ++j)if(!vis[j] && (res == 0 || check(d[j] - las, d[res] - las)))res = j;
		vis[ans[i] = res] = true; las = d[res];
	}
	for(int i = 1; i <= n; ++i)printf("%d ",ans[i]); printf("\n");
	return 0;
}

C. 11:23

写个模拟器,然后手玩

抑智小游戏

根据提解说的稍微有点策略

下附游戏模拟器

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; cin >> x; return x;
}
const int maxn = 105;
int n, m;
int s[maxn][maxn], d[maxn][maxn];
bool rr[maxn][maxn], rd[maxn][maxn];
int lv[maxn][maxn];
struct re{
	int a[4];
	void init(){a[0] = read(), a[1] = read(), a[2] = read(), a[3] = read();}
	friend bool operator < (const re &x, const re &y){return x.a[0] < y.a[0] || x.a[1] < y.a[1] || x.a[2] < y.a[2] || x.a[3] < y.a[3];}
	friend re operator + (const re &x, const re &y){return {x.a[0] + y.a[0] , x.a[1] + y.a[1] , x.a[2] + y.a[2] , x.a[3] + y.a[3]};}
	friend re operator - (const re &x, const re &y){return {x.a[0] - y.a[0] , x.a[1] - y.a[1] , x.a[2] - y.a[2] , x.a[3] - y.a[3]};}
}f, c, r, now;
int k, g, spead;
int rt[100005];
void print(int tim){
	system("clear");
	printf("spead : %d \n",spead);
	printf("room : %d %d %d %d %s\n",f.a[0], f.a[1], f.a[2], f.a[3], now < f ? "NO" : "YES");
	printf("city : %d %d %d %d %s\n",c.a[0], c.a[1], c.a[2], c.a[3], now < c ? "NO" : "YES");
	printf("road : %d %d %d %d %s\n",r.a[0], r.a[1], r.a[2], r.a[3], now < r ? "NO" : "YES");
	printf("res : %d %d %d %d\n",now.a[0], now.a[1], now.a[2], now.a[3]);
	printf("trans : %d\n",k);
	printf("day %d \nnext %d %d %d %d %d %d %d %d %d %d\n",tim - 1, rt[tim], rt[tim + 1], rt[tim + 2], rt[tim + 3], rt[tim + 4], rt[tim + 5], rt[tim + 6], rt[tim + 7], rt[tim + 8], rt[tim + 9]);
	printf("  ");
	for(int j = 0; j <= m; ++j){
		printf("%d",j);
		if(j != m){printf("          ");}
	}
	printf("\n");
	for(int i = 0; i <= n; ++i){
		printf("%d ",i);
		for(int j = 0; j <= m; ++j){
			if(lv[i][j] == 1)printf("W");
			if(lv[i][j] == 2)printf("C");
			if(lv[i][j] == 0)printf("*");
			if(j != m){
				if(rr[i][j])printf("==========");
				else printf("----------");
			}
		}
		printf("\n");
		if(i != n){
			for(int cnt = 1; cnt <= 5; ++cnt){
				printf("  ");
				for(int j = 0; j <= m; ++j){
					if(rd[i][j])printf("+"); else printf("|");
					if(j != m){
						if(cnt == 3)printf(" [%d , %2d] ",s[i + 1][j + 1], d[i + 1][j + 1]);
						else printf("          ");
					}
				}
				printf("\n");
			}
		}
	}
}
vector<char>out;
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			s[i][j] = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			d[i][j] = read();
	f.init(); c.init(); r.init();
	k = read(), g = read();
	for(int i = 1; i <= g; ++i)rt[i] = read();
	
	now = f;
	for(int i = 1; i <= 1000; ++i){
		print(i);
		char ch; cin >> ch; out.push_back(ch); 
		if(spead >= 10){
			printf("%d\n",i - 1);	
			break;
		}
		while(ch != 'E'){
			if(ch == 'B'){
				int x = read(), y = read(); 
				out.push_back(' ');
				out.push_back(x + '0');
				out.push_back(' ');
				out.push_back(y + '0'); 
				if(now < f){cerr << "not enough" << endl;goto X;}
				now = now - f;
				if(lv[x][y]){cerr << "the pos has sth" << endl;goto X;}
				lv[x][y] = 1;
				++spead;
			}else if(ch == 'C'){
				int x = read(), y = read(); 
				out.push_back(' ');
				out.push_back(x + '0'); 
				out.push_back(' ');
				out.push_back(y + '0');
				if(now < c + f){cerr << "not enough" << endl;goto X;}	
				now = now - c - f;
				if(lv[x][y]){cerr << "the pos has sth" << endl;goto X;}
				lv[x][y] = 2;
				spead += 2;

			}else if(ch == 'U'){
				int x = read(), y = read(); 
				out.push_back(' ');
				out.push_back(x + '0'); 
				out.push_back(' ');
				out.push_back(y + '0');
				if(now < c){cerr << "not enough" << endl;goto X;}	
				now = now - c;
				if(lv[x][y] != 1){cerr << "the pos is not a room" << endl;goto X;}
				lv[x][y] = 2;
				++spead;
			}else if(ch == 'R'){
				int x = read(), y = read(), xx = read(), yy = read();
				out.push_back(' ');
				out.push_back(x + '0'); 
				out.push_back(' ');
				out.push_back(y + '0');
				out.push_back(' ');
				out.push_back(xx + '0'); 
				out.push_back(' ');
				out.push_back(yy + '0');
				if(now < r){cerr << "not enough" << endl;goto X;}	
				now = now - r;
				if(xx + yy < x + y)swap(x, xx), swap(y, yy);
				if(x == xx)rr[x][y] = 1;
				else rd[x][y] = 1;
			}else if(ch == 'X'){
				int p = read(), q = read();
				out.push_back(' ');
				out.push_back(p + '0'); 
				out.push_back(' ');
				out.push_back(q + '0');
				if(now.a[p - 1] < k){cerr << "not enough" << endl;goto X;}
				else now.a[p - 1] -= k, now.a[q - 1] += 1;
			}
			print(i);
			out.push_back('\n');
			cin >> ch; out.push_back(ch); 
			if(spead >= 10){
				printf("all = %d\n",i - 1); break;
			}
		}
		out.push_back('\n');
		for(int p = 1; p <= n; ++p)
			for(int q = 1; q <= m; ++q)
				if(d[p][q] == rt[i]){
					int cnt = 0;
					cnt += lv[p][q] + lv[p - 1][q - 1] + lv[p - 1][q] + lv[p][q - 1];
					now.a[s[p][q] - 1] += cnt;
				}
	}
	X:;
	for(char c : out)printf("%c",c);
	return 0;
}

2023冲刺清北营12

∑了三个代码。。。。

我TM在干啥啊

A. 出题人

如果有偶数,容易构造。

否则

bi 看成点,那么每个 ai 看成一条边

n 个点 n 条边必然有环

那么就是找环

考虑环上

ai=2bi

bi 全是奇数,那么是偶环

ai=ibi=ibi

那就是找两个等大的和相同的集合

直接做 3n 难以接受

折半搜索

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;

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 = 35;
int n;
ll p3[maxn], a[maxn], b[maxn];
vector<pll>v[maxn + maxn];
vector<int>tmp[3];
void print(ll s1, ll s2){
	printf("Yes\n");
	for(int i = 0; i < n / 2; ++i)tmp[s1 % 3].push_back(i), s1 /= 3;
	for(int i = n / 2; i < n; ++i)tmp[s2 % 3].push_back(i), s2 /= 3;
	int cnt = 0; b[++cnt] = a[tmp[1][0]]; b[++cnt] = 0;
	for(int i = 0; i < (int)tmp[1].size() - 1; ++i){
		b[cnt + 1] = a[tmp[2][i]] - b[cnt]; ++cnt;
		b[cnt + 1] = a[tmp[1][i + 1]] - b[cnt]; ++cnt;
	}
	for(int i : tmp[0])b[++cnt] = a[i] - b[1];
	for(int i = 1; i <= n; ++i)printf("%lld ",b[i]); printf("\n");
	for(int i = 0; i < n; ++i){
		int fl = 0;
		for(int j = 1; j <= n && fl == 0; ++j)
			for(int k = 1; k <= n && fl == 0; ++k)
				if(a[i] == b[j] + b[k])printf("%d %d\n", j, k), fl = 1;
	}
	exit(0);
}
void dfs1(int x, ll sum, ll s, int si){
	if(x == n / 2){
		if(s)v[si + 30].push_back({sum, s});
		if(sum == 0 && si == 0 && s)print(s, 0);
		return;
	}
	dfs1(x + 1, sum, s, si);
	dfs1(x + 1, sum + a[x], s + p3[x], si + 1);
	dfs1(x + 1, sum - a[x], s + 2 * p3[x], si - 1);
}
void dfs2(int x, ll sum, ll s, int si){
	if(x == n){
		auto it = lower_bound(v[30 - si].begin(), v[30 - si].end(), pll(-sum, -1e18));
		if(it != v[30 - si].end() && (*it).first == -sum)print((*it).second, s);
		else if(sum == 0 && si == 0 && s)print(0, s);
		return;
	}
	dfs2(x + 1, sum, s, si);
	dfs2(x + 1, sum + a[x], s + p3[x - n / 2], si + 1);
	dfs2(x + 1, sum - a[x], s + 2 * p3[x - n / 2], si - 1);
}
int main(){
	freopen("problemsetter.in","r",stdin);
	freopen("problemsetter.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
	int rpos = 0;
	for(int i = 1; i <= n; ++i)if(!(a[i] & 1)){rpos = i;break;}
	if(rpos){
		ll val = a[rpos] / 2;
		printf("Yes\n");
		for(int i = 1; i <= n; ++i)printf("%lld ",a[i] - val);
		printf("\n");
		for(int i = 1; i <= n; ++i)printf("%d %d\n",i, rpos);
		return 0;
	}
	for(int i = 0; i < n; ++i)a[i] = a[i + 1];
	p3[0] = 1; for(int i = 1; i < n / 2; ++i)p3[i] = 3ll * p3[i - 1];	
	dfs1(0, 0, 0, 0);
	for(int i = 1; i <= 60; ++i)sort(v[i].begin(), v[i].end());
	dfs2(n / 2, 0, 0, 0);
	printf("No\n");
	return 0;
}

B. 彩色挂饰

建立圆方树,fx,c 表示 x 节点取到 c 颜色,其子树内连通块同色数最小值

每次处理一个点双,暴力枚举颜色即可通过,但是显然可以被卡

正解

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 col = getchar();
	while(!isdigit(col))col = getchar();
	do{x = x * 10 + (col ^ 48); col = getchar();}while(isdigit(col));
	return x;
}
const int maxn = 2e5 + 55, inf = 0x3f3f3f3f;
int n, m, k, s, col[maxn], c[maxn], cnt;
vector<int>g[maxn], e[maxn];
int tim, low[maxn], dfn[maxn], st[maxn], top;
bool vis[maxn];
struct DSU{
	int f[maxn];
	int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
}S;

void tarjan(int x, int fa){
	dfn[x] = low[x] = ++tim;
	st[++top] = x;
	for(int v : g[x])if(v != fa){
		if(!dfn[v]){
			tarjan(v, x);
			low[x] = min(low[x], low[v]);
			if(low[v] >= dfn[x]){
				int y; ++cnt;
				do{
					y = st[top--];
					e[cnt].push_back(y);
					e[y].push_back(cnt);
				}while(y != v);
				e[cnt].push_back(x); e[x].push_back(cnt);
			}
		}else low[x] = min(low[x], dfn[v]);
	}
}
int f[maxn][22], tmp[22], rf;
vector<pii>rem;
void dfs2(int x, int si, int res){
	if(si == e[x].size()){
		for(int v : e[x])S.f[v] = v;
		for(pii v : rem)if(col[v.first] == col[v.second] && S.fa(v.first) != S.fa(v.second)){
			--res; S.f[S.fa(v.first)] = S.fa(v.second);
		}
		tmp[col[rf]] = min(tmp[col[rf]], res);
		return;
	}
	if(c[e[x][si]]){
		col[e[x][si]] = c[e[x][si]];
		dfs2(x, si + 1, res + f[e[x][si]][c[e[x][si]]]);
	}else{
		for(int c = 1; c <= k; ++c){
			col[e[x][si]] = c;
			dfs2(x, si + 1, res + f[e[x][si]][c]);
		}
	}
}
void dfs1(int x, int fa){
	if(x <= n){
		if(col[x]){
			for(int i = 1; i <= k; ++i)f[x][i] = i == col[x] ? 1 : inf;
		}else{
			for(int i = 1; i <= k; ++i)f[x][i] = 1;
		}
		for(int v : e[x])if(v != fa)dfs1(v, x);
	}else{
		for(int v : e[x])if(v != fa)dfs1(v, x);
		for(int v : e[x])vis[v] = true;
		rem.clear();
		for(int v : e[x])for(int u : g[v])if(vis[u])rem.push_back(pii(v, u));
		for(int i = 1; i <= 20; ++i)tmp[i] = inf;
		rf = fa;
		dfs2(x, 0, 0);
		for(int v : e[x])vis[v] = false;
    	for(int i = 1; i <= 20; ++i)f[fa][i] = tmp[i];
	}
}
int main(){
	// freopen("colorful.in","r",stdin);
	// freopen("colorful.out","w",stdout);
	n = read(), m = read(), k = read(), s = read();
	for(int i = 1; i <= n; ++i)c[i] = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	cnt = n; tarjan(1, 0); dfs1(1, 1);
	int ans = inf; for(int i = 1; i <= k; ++i)ans = min(ans, f[1][i]);
	printf("%d\n",ans);
	return 0;
}

C. 逆转函数

枚举对称中心可以做到 n2

感觉跟回文很像,使用类似 manachar 的做法

维护 pre/nxt 为某个数的前驱后继,那么就不用开数组记录映射关系

当直接继承右端点超过最远点时暴力删除的复杂度是正确的

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 = 6e5 + 55, mod = 998244353;
int n, m, a[maxn], ans[maxn], pre[maxn], suf[maxn], tub[maxn], res;
int cnt[maxn], L[maxn], R[maxn];
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int main(){
	freopen("invfunc.in","r",stdin);
	freopen("invfunc.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i + i - 1] = read();
	n = n + n - 1;
	for(int i = 1; i <= n; ++i){
		pre[i] = tub[a[i]];
		tub[a[i]] = i;
	}
	for(int i = 0; i <= m; ++i)tub[i] = n + 1; 
	for(int i = n; i >= 1; --i){
		suf[i] = tub[a[i]];
		tub[a[i]] = i;
	}
	tub[0] = 1;
	for(int i = 1; i <= m; ++i)tub[i] = 1ll * tub[i - 1] * m % mod;
	int l = 1, r = 0;
	for(int i = 1; i <= n; ++i){
		L[i] = R[i] = i;
		if(a[i])cnt[i] = 1, ans[i] = tub[m - 1];
		if(i <= r){
			L[i] = l + r - R[l + r - i];
			R[i] = l + r - L[l + r - i];
			cnt[i] = cnt[l + r - i];
			ans[i] = ans[l + r - i];
			for(int j = 0; R[i] > r; ++j, --R[i], ++L[i])if(a[L[l + r - i] + j]){
				add(ans[i], mod - tub[m - cnt[i]]);
				cnt[i] -= (suf[L[l + r - i] + j] >= R[l + r - i] - j);
				cnt[i] -= (pre[R[l + r - i] - j] < L[l + r - i] + j);
			}
		}
		while(L[i] > 1 && R[i] < n){
			--L[i]; ++R[i];
			if((suf[L[i]] < R[i] && a[R[i] + L[i] - suf[L[i]]] != a[R[i]]) || (pre[R[i]] > L[i] && a[R[i] + L[i] - pre[R[i]]] != a[L[i]])){
				++L[i]; --R[i]; break;
			}
			if(a[L[i]]){
				cnt[i] += (suf[L[i]] >= R[i]);
				cnt[i] += (pre[R[i]] < L[i]);
				add(ans[i], tub[m - cnt[i]]);
			}
		}
		add(res, ans[i]);
		if(r < R[i] || (r == R[i] && l < L[i]))l = L[i], r = R[i];
	}
	printf("%d\n",res);
	return 0;
}
posted @   Chen_jr  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示