JSOI2016部分题题解

48 篇文章 0 订阅
12 篇文章 0 订阅

边做边更吧。。。


最佳团体

分数规划+ t r e e d p treedp treedp 即可。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <algorithm>

#define eps 1e-6
using namespace std;
typedef long long LL;
double _max(double x, double y) {return x > y ? x : y;}
double _min(double x, double y) {return x < y ? x : y;}
const int N = 2501;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

struct edge {
	int x, y, next;
} e[2 * N]; int len, last[N];
int cnt, K, n, s[N], p[N], tot[N];
double c[N], f[N][N], l[N];

void ins(int x, int y) {
	e[++len].x = x, e[len].y = y;
	e[len].next = last[x], last[x] = len;
}

void treedp(int x) {
	f[x][0] = 0, f[x][1] = c[x]; tot[x] = 1;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		treedp(y);
		for(int i = 1; i <= tot[x] + tot[y]; i++) l[i] = -999999999;
		for(int i = 1; i <= tot[x]; i++) {
			for(int j = 0; j <= tot[y]; j++) {
				l[i + j] = _max(l[i + j], f[x][i] + f[y][j]);
			}
		} tot[x] += tot[y];
		for(int i = 1; i <= tot[x]; i++) f[x][i] = l[i];
	}
}

bool check(double mid) {
	memset(c, 0, sizeof(c));
	for(int i = 1; i <= n; i++) c[i] = (double)s[i] - mid * p[i];
	treedp(0);
	return f[0][K + 1] >= 0;
}

int main() {
	K = read(), n = read();
	for(int i = 1; i <= n; i++) {
		p[i] = read(), s[i] = read();
		int x = read(); ins(x, i);
	} double l = 0, r = 1e4, ans;
	while(r - l >= eps) {
		double mid = (l + r) / 2.0;
		if(check(mid)) l = mid + eps, ans = mid;
		else r = mid - eps;
	} printf("%.3lf\n", ans);
	return 0;
}


独特的树叶

判断两棵树是否相同可以使用树 H a s h Hash Hash
我用的 H a s h Hash Hash方式是按照子树大小来 H a s h Hash Hash
然后你搞一个换根 D P DP DP判一下即可。


#include <map>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 100002;
const LL P1 = 71, P2 = 131;
const LL mod = 1e9 + 7;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

struct edge {
	int x, y, next;
} e[2 * N]; int len, last[N];
map<LL, bool> mp1, mp2;
int n, tot[N], ru[N];
LL Hash1[N], Hash2[N];
int ms;

void ins(int x, int y) {
	e[++len].x = x, e[len].y = y;
	e[len].next = last[x], last[x] = len;
}

LL pow_mod(LL a, int k) {
	LL ans = 1;
	while(k) {
		if(k & 1) (ans *= a) %= mod;
		(a *= a) %= mod; k /= 2;
	} return ans;
}

void dfs1(int x, int fa) {
	tot[x] = 1; Hash1[x] = Hash2[x] = 0;
	LL cnt1 = 1, cnt2 = 1;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(y != fa) {
			dfs1(y, x);
			tot[x] += tot[y];
			(cnt1 *= Hash1[y]) %= mod;
			(cnt2 *= Hash2[y]) %= mod;
		}
	} cnt1 = (LL)(cnt1 + tot[x]) * P1 % mod, cnt2 = (LL)(cnt2 + tot[x]) * P2 % mod;
	Hash1[x] = cnt1, Hash2[x] = cnt2;
}

void dfs2(int x, int fa) {
	mp1[Hash1[x]] = mp2[Hash2[x]] = 1;
	LL zz1 = (Hash1[x] - P1 * n % mod + mod) % mod;
	LL zz2 = (Hash2[x] - P2 * n % mod + mod) % mod;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(y != fa) {
			LL g1 = (zz1 * pow_mod(Hash1[y], mod - 2) % mod + P1 * (n - tot[y]) % mod) % mod;
			LL g2 = (zz2 * pow_mod(Hash2[y], mod - 2) % mod + P2 * (n - tot[y]) % mod) % mod;
			Hash1[y] = ((Hash1[y] - P1 * tot[y] % mod + mod) % mod * g1 % mod + P1 * n % mod) % mod;
			Hash2[y] = ((Hash2[y] - P2 * tot[y] % mod + mod) % mod * g2 % mod + P2 * n % mod) % mod;
			dfs2(y, x);
		}
	}
}

void dfs3(int x, int fa) {
	LL zz1 = (Hash1[x] - P1 * (n + 1) % mod + mod) % mod;
	LL zz2 = (Hash2[x] - P2 * (n + 1) % mod + mod) % mod;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(y != fa) {
			LL g1 = (zz1 * pow_mod(Hash1[y], mod - 2) % mod + P1 * (n + 1 - tot[y]) % mod) % mod;
			LL g2 = (zz2 * pow_mod(Hash2[y], mod - 2) % mod + P2 * (n + 1 - tot[y]) % mod) % mod;
			if(x == 1 && ru[x] == 1) {
				if(mp1[Hash1[y]] && mp2[Hash2[y]]) ms = _min(ms, x);
			} if(ru[y] == 1 && mp1[g1] && mp2[g2]) ms = _min(ms, y);
			Hash1[y] = ((Hash1[y] - P1 * tot[y] % mod + mod) % mod * g1 % mod + P1 * (n + 1) % mod) % mod;
			Hash2[y] = ((Hash2[y] - P2 * tot[y] % mod + mod) % mod * g2 % mod + P2 * (n + 1) % mod) % mod;
			dfs3(y, x);
		}
	}
}

int main() {
	n = read();
	for(int i = 1; i < n; i++) {
		int x = read(), y = read();
		ins(x, y), ins(y, x);
	} dfs1(1, 0), 
	dfs2(1, 0);
	len = 0; memset(last, 0, sizeof(last));
	for(int i = 1; i <= n; i++) {
		int x = read(), y = read();
		ins(x, y), ins(y, x), ru[x]++, ru[y]++;
	} dfs1(1, 0), ms = 999999999, 
	dfs3(1, 0);
	printf("%d\n", ms);
	return 0;
}


扭动的回文串

容易发现一个结论,以 A A A串为例,答案肯定就是就是以某个点为中心在 A A A串中的最长回文串,在 B B B串中往右拓一段, A A A串中往左拓一段,然后直接二分 H a s h Hash Hash


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 100003;
const ULL P = 233;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

int m1[N], m2[N];
ULL s1[2][N], s2[2][N], o[N];
char ss1[N], ss2[N];

int main() {
	int n = read();
	scanf("%s", ss1 + 1);
	scanf("%s", ss2 + 1);
	s1[0][0] = 0, s2[0][0] = 0;
	for(int i = 1; i <= n; i++) s1[0][i] = s1[0][i - 1] * P + ss1[i], s2[0][i] = s2[0][i - 1] * P + ss2[i];
	s1[1][n + 1] = s2[1][n + 1] = 0;
	for(int i = n; i >= 1; i--) s1[1][i] = s1[1][i + 1] * P + ss1[i], s2[1][i] = s2[1][i + 1] * P + ss2[i];
	o[0] = 1; for(int i = 1; i <= n; i++) o[i] = o[i - 1] * P;
	int ans = 0;
	for(int i = 1; i <= n; i++) {
		int l = 1, r = _min(i, n - i + 1), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s1[1][i] - s1[1][i + mid] * o[mid] == s1[0][i] - s1[0][i - mid] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} m1[i] = hh;
		ans = _max(ans, hh * 2 - 1);
	} for(int i = 1; i <= n; i++) {
		int l = 1, r = _min(i, n - i + 1), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s2[1][i] - s2[1][i + mid] * o[mid] == s2[0][i] - s2[0][i - mid] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} m2[i] = hh;
		ans = _max(ans, hh * 2 - 1);
	} for(int i = 1; i <= n; i++) {
		int l = 1, r = _min(i - m1[i], n - (i + m1[i] - 1) + 1), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s1[0][i - m1[i]] - s1[0][i - m1[i] - mid] * o[mid] == s2[1][i + m1[i] - 1] - s2[1][i + m1[i] + mid - 1] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} ans = _max(ans, (m1[i] + hh) * 2 - 1);
	}  for(int i = 1; i <= n; i++) {
		int l = 1, r = _min(i - m2[i] + 1, n - (i + m2[i] - 1)), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s2[1][i + m2[i]] - s2[1][i + m2[i] + mid] * o[mid] == s1[0][i - m2[i] + 1] - s1[0][i - m2[i] - mid + 1] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} ans = _max(ans, (m2[i] + hh) * 2 - 1);
	} for(int i = 1; i < n; i++) {
		int l = 1, r = _min(i, n - i), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s1[0][i] - s1[0][i - mid] * o[mid] == s1[1][i + 1] - s1[1][i + mid + 1] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} m1[i] = hh;
		ans = _max(ans, hh * 2);
	} for(int i = 1; i < n; i++) {
		int l = 1, r = _min(i, n - i), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s2[0][i] - s2[0][i - mid] * o[mid] == s2[1][i + 1] - s2[1][i + mid + 1] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} m2[i] = hh;
		ans = _max(ans, hh * 2);
	} for(int i = 1; i < n; i++) {
		int l = 1, r = _min(i - m1[i], n - (i + m1[i]) + 1), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s1[0][i - m1[i]] - s1[0][i - m1[i] - mid] * o[mid] == s2[1][i + m1[i]] - s2[1][i + m1[i] + mid] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} ans = _max(ans, (hh + m1[i]) * 2);
	} for(int i = 1; i < n; i++) {
		int l = 1, r = _min(i - m2[i] + 1, n - (i + m2[i])), hh = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s2[1][i + m2[i] + 1] - s2[1][i + m2[i] + mid + 1] * o[mid] == s1[0][i - m2[i] + 1] - s1[0][i - m2[i] - mid + 1] * o[mid]) l = mid + 1, hh = mid;
			else r = mid - 1;
		} ans = _max(ans, (hh + m2[i]) * 2);
	} printf("%d\n", ans);
	return 0;
}


灯塔

后面那部分不同的值不超过根号,然后我就写了个根号就过了。
看了一下 L O J LOJ LOJ的评论,这好像不是正解,想了一下这种东西应该是满足决策单调性的吧,不写了。。。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

int f[20][100001], Log[100001], bin[20];

int getmx(int l, int r) {
	int hh = Log[r - l + 1];
	return _max(f[hh][l], f[hh][r - bin[hh] + 1]);
}

int main() {
	int n = read();
	bin[0] = 1; for(int i = 1; i <= 18; i++) bin[i] = bin[i - 1] << 1;
	Log[1] = 0; for(int i = 2; i <= n; i++) Log[i] = Log[i >> 1] + 1;
	for(int i = 1; i <= n; i++) f[0][i] = read();
	for(int i = 1; i <= 18; i++) for(int j = 1; j <= n - bin[i] + 1; j++) f[i][j] = _max(f[i - 1][j], f[i - 1][j + bin[i - 1]]);
	for(int i = 1; i <= n; i++) {
		int ans = 0, z = 1;
		while(1) {
			int r = i - (z - 1) * (z - 1) - 1;
			if(r < 1) break;
			int l = _max(1, i - z * z);
			ans = _max(ans, getmx(l, r) + z);
			z++;
		} z = 1;
		while(1) {
			int l = i + (z - 1) * (z - 1) + 1;
			if(l > n) break;
			int r = _min(n, i + z * z);
			ans = _max(ans, getmx(l, r) + z);
			z++;
		} ans -= f[0][i];
		put(_max(ans, 0)), puts("");
	} return 0;
}


位运算

f [ S ] f[S] f[S] S S S表示 n n n个位置是否顶着上界,这样子转移的话,会不满足 n n n个数互不相同的限制。
考虑更改一下状态,让序列变得有序,设 f [ S ] f[S] f[S]为每一位是否跟后一位相同,最后一位表示是否顶格,这样矩乘一下即可。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const LL mod = 1e9 + 7;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

int n, k;
char ss[51];
int bin[10], one[1 << 7];
struct matrix {
	int f[128][128];
	matrix() {memset(f, 0, sizeof(f));}
	
	friend matrix operator * (matrix a, matrix b) {
		matrix c;
		for(int i = 0; i < bin[n]; i++) {
			for(int j = 0; j < bin[n]; j++) {
				for(int k = 0; k < bin[n]; k++) {
					(c.f[i][j] += (LL)a.f[i][k] * b.f[k][j] % mod) %= mod;
				}
			}
		} return c;
	}
} ans, sum, now;

int main() {
	n = read(), k = read();
	scanf("%s", ss + 1);
	int S = strlen(ss + 1);
	bin[0] = 1; for(int i = 1; i <= 7; i++) bin[i] = bin[i - 1] << 1;
	for(int i = 0; i < bin[n]; i++) {
		sum.f[i][i] = 1;
		if(i) one[i] = one[i >> 1] + (i & 1);
	} ans.f[0][bin[n] - 1] = 1;
	for(int i = 1; i <= S; i++) {
		memset(now.f, 0, sizeof(now.f));
		for(int j = 0; j < bin[n]; j++) {
			for(int k = 0; k < bin[n]; k++) if(!(one[k] & 1)){
				int s = 0; bool bk = 0;
				if(ss[i] == '0') {
					if(j & bin[n - 1]) {
						if(k & bin[n - 1]) break;
						s += bin[n - 1];
						if(bk) continue;
						for(int u = 1; u < n; u++) {
							if((j & bin[u - 1])) {
								if((k & bin[u - 1]) && !(k & bin[u])) {bk = 1; break;}
								if((k & bin[u - 1]) && (k & bin[u])) s += bin[u - 1];
								else if(!(k & bin[u - 1]) && !(k & bin[u])) s += bin[u - 1];
							}
						} if(bk) continue;
					} else {
						for(int u = 1; u < n; u++) {
							if((j & bin[u - 1])) {
								if((k & bin[u - 1]) && !(k & bin[u])) {bk = 1; break;}
								if((k & bin[u - 1]) && (k & bin[u])) s += bin[u - 1];
								else if(!(k & bin[u - 1]) && !(k & bin[u])) s += bin[u - 1];
							}
						} if(bk) continue;
					}
				} else {
					if(j & bin[n - 1]) {
						if(k & bin[n - 1]) s += bin[n - 1];
						for(int u = 1; u < n; u++) {
							if((j & bin[u - 1])) {
								if((k & bin[u - 1]) && !(k & bin[u])) {bk = 1; break;}
								if((k & bin[u - 1]) && (k & bin[u])) s += bin[u - 1];
								else if(!(k & bin[u - 1]) && !(k & bin[u])) s += bin[u - 1];
							}
						} if(bk) continue;
					} else {
						for(int u = 1; u < n; u++) {
							if((j & bin[u - 1])) {
								if((k & bin[u - 1]) && !(k & bin[u])) {bk = 1; break;}
								if((k & bin[u - 1]) && (k & bin[u])) s += bin[u - 1];
								else if(!(k & bin[u - 1]) && !(k & bin[u])) s += bin[u - 1];
							}
						} if(bk) continue;
					}
				} now.f[j][s]++;
			}
		} sum = sum * now;
	} while(k) {
		if(k & 1) ans = ans * sum;
		sum = sum * sum; k /= 2;
	} put(ans.f[0][0]);
	return 0;
}


飞机调度

f l o y d floyd floyd一下,就是最小链覆盖的模型。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

struct edge {
	int x, y, next;
} e[501 * 501]; int len, last[501];
struct node {int x, y, d;} b[501];
int tt, match[501], chw[501];
int a[501], f[501][501], bak[501][501];

void ins(int x, int y) {
	e[++len].x = x, e[len].y = y;
	e[len].next = last[x], last[x] = len;
}

bool findmuniu(int x) {
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(chw[y] != tt) {
			chw[y] = tt;
			if(!match[y] || findmuniu(match[y])) {
				match[y] = x; return 1;
			}
		}
	} return 0;
}

int main() {
	int n = read(), m = 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++) {
			bak[i][j] = f[i][j] = read();
			if(i != j) f[i][j] += a[j];
		}
	} for(int k = 1; k <= n; k++) {
		for(int i = 1; i <= n; i++) if(i != k){
			for(int j = 1; j <= n; j++) if(i != j && j != k){
				f[i][j] = _min(f[i][j], f[i][k] + f[k][j]);
			}
		}
	} for(int i = 1; i <= m; i++) b[i].x = read(), b[i].y = read(), b[i].d = read();
	for(int i = 1; i <= m; i++) for(int j = 1; j <= m; j++) if(i != j){
		if(b[i].d + bak[b[i].x][b[i].y] + a[b[i].y] + f[b[i].y][b[j].x] <= b[j].d) ins(i, j);
	} int ans = 0;
	for(int i = 1; i <= m; i++) {
		tt++; if(findmuniu(i)) ans++;
	} printf("%d\n", m - ans);
	return 0;
}


无界单词

第一问的话考虑设 f [ i ] f[i] f[i]表示长度为 i i i的无界单词合法个数,
考虑容斥,则:
f [ i ] = 2 i − ∑ j = 1 i / 2 f [ j ] ∗ 2 i − 2 j f[i]=2^{i}-\sum_{j=1}^{i/2}f[j]*2^{i-2j} f[i]=2ij=1i/2f[j]2i2j
为什么只用枚举到 i / 2 i/2 i/2?如果大于 i / 2 {i/2} i/2就不会有合法无界单词的了。。。
第二问用类似的方法去 D P DP DP,考虑按位确定,若当前有 l e n len len位已经确定了。
转移 i i i的时候,对 j j j分类讨论一下:
i &lt; = l e n i&lt;=len i<=len,因为前 l e n len len位已经确定,所以直接判是否合法即可。
j &gt; = l e n j&gt;=len j>=len,则 f [ j ] f[j] f[j]也已经确定,且不受前 l e n len len位影响,直接转移即可。
j + l e n &lt; = i j+len&lt;=i j+len<=i,说明后 j j j位与前 l e n len len位无交,直接转移。
否则,说明与前 l e n len len位有交,直接判断合法即可。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
ULL read() {
	ULL s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(ULL x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

ULL K, bin[65], f[65];
int n, cnt, p[65], a[65];

void getnxt(int i) {
	if(i == 1) {p[1] = 0; return ;}
	int j = p[i - 1];
	while(j && a[j + 1] != a[i]) j = p[j];
	p[i] = j + (a[j + 1] == a[i]);
}

ULL chk(int i) {return p[i] == 0;}
ULL chk2(int x) {
	for(int i = 1; i <= x; i++) if(a[i] != a[cnt - x + i]) return 0;
	return 1;
}

ULL solve() {
	for(int i = 1; i <= n; i++) {
		if(i <= cnt) f[i] = chk(i);
		else {
			f[i] = bin[i - cnt];
			for(int j = 1; j * 2 <= i; j++) {
				if(j >= cnt) f[i] -= f[j] * bin[i - j * 2];
				else if(j + cnt <= i) f[i] -= f[j] * bin[i - j - cnt];
				else f[i] -= f[j] * chk2(j + cnt - i);
			}
		}
	} return f[n];
}

int main() {
	int tt = read();
	bin[0] = 1LL; for(int i = 1; i <= 64; i++) bin[i] = bin[i - 1] << 1;
	while(tt--) {
		n = read(), K = read();
		cnt = 0; LL ans = solve();
		put(ans), puts("");
		for(int i = 1; i <= n; i++) {
			a[i] = 0, getnxt(i); cnt++;
			ULL u = solve();
			if(K > u) K -= u, a[i] = 1, getnxt(i);
		} for(int i = 1; i <= n; i++) putchar(a[i] + 'a');
		puts("");
	} return 0;
}


轻重路径

这题你可以尝试反着做,然后就相当于把一条路径上的一部分轻链变成重链。
树剖一下,你要开一颗线段树维护每一个点与父亲的连边是否是轻链,开一颗线段树维护子树内当前最晚被删掉的点,还要维护当前每个点的子树大小,好烦啊。。。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 200001;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(LL x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

int n;
struct Bit {
	int s[N];
	
	int lowbit(int x) {return x & -x;}
	void change(int x, int c) {for(int i = x;i <= n; i += lowbit(i)) s[i] += c;}
	int getsum(int x) {int sum = 0; for(int i = x; i; i -= lowbit(i)) sum += s[i]; return sum;}
} s;
int tp, sta[N]; LL ans;
struct seg1 {
	int cnt, lc[N * 2], rc[N * 2], c[N * 2];
	
	void bt(int l, int r) {
		int now = ++cnt;
		lc[now] = rc[now] = -1;
		if(l < r) {
			int mid = (l + r) / 2;
			lc[now] = cnt + 1; bt(l, mid);
			rc[now] = cnt + 1; bt(mid + 1, r);
		}
	}
	
	void change(int now, int l, int r, int p, int C) {
		if(l == r) {c[now] = C; return ;}
		int mid = (l + r) / 2;
		if(p <= mid) change(lc[now], l, mid, p, C);
		else change(rc[now], mid + 1, r, p, C);
		c[now] = c[lc[now]] + c[rc[now]];
	}
	
	void get(int now, int l, int r, int ll, int rr) {
		if(l == r) {sta[++tp] = l; return ;}
		int mid = (l + r) / 2;
		if(l == ll && r == rr) {
			if(c[lc[now]]) get(lc[now], l, mid, ll, mid);
			if(c[rc[now]]) get(rc[now], mid + 1, r, mid + 1, rr);
			return ;
		} if(rr <= mid) {
			if(c[lc[now]]) get(lc[now], l, mid, ll, rr);
		}
		else if(ll > mid) {
			if(c[rc[now]]) get(rc[now], mid + 1, r, ll, rr);
		}
		else {
			if(c[lc[now]]) get(lc[now], l, mid, ll, mid);
			if(c[rc[now]]) get(rc[now], mid + 1, r, mid + 1, rr);
		}
	}
} t;
struct seg2 {
	int cnt, lc[N * 2], rc[N * 2], c[N * 2];
	
	void bt(int l, int r) {
		int now = ++cnt;
		lc[now] = rc[now] = -1;
		c[now] = 0;
		if(l < r) {
			int mid = (l + r) / 2;
			lc[now] = cnt + 1; bt(l, mid);
			rc[now] = cnt + 1; bt(mid + 1, r);
		}
	}
	
	void change(int now, int l, int r, int p, int C) {
		if(l == r) {c[now] = C; return ;}
		int mid = (l + r) / 2;
		if(p <= mid) change(lc[now], l, mid, p, C);
		else change(rc[now], mid + 1, r, p, C);
		c[now] = _max(c[lc[now]], c[rc[now]]);
	}
	
	int get(int now, int l, int r, int ll, int rr) {
		if(l == ll && r == rr) return c[now];
		int mid = (l + r) / 2;if(rr <= mid) return get(lc[now], l, mid, ll, rr);
		else if(ll > mid) return get(rc[now], mid + 1, r, ll, rr);
		else return _max(get(lc[now], l, mid, ll, mid), get(rc[now], mid + 1, r, mid + 1, rr));
	}
} t2;
struct edge {
	int x, y, next;
} e[N]; int len, last[N];
int q[N];
int L[N], R[N], ll[N], rr[N], g[N];
int id, fa[N], tot[N], dep[N], son[N], top[N], ys[N], bak[N];
int zz = 0; LL pp[N];
int Son[N];

void ins(int x, int y) {
	e[++len].x = x, e[len].y = y;
	e[len].next = last[x], last[x] = len;
}

void pre_tree_node(int x) {
	tot[x] = 1; ll[x] = ++id;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		dep[y] = dep[x] + 1;
		fa[y] = x;
		pre_tree_node(y);
		tot[x] += tot[y];
		if(tot[y] > tot[son[x]]) son[x] = y;
	} rr[x] = id;
}

void pre_tree_edge(int x, int tp) {
	ys[x] = ++id, top[x] = tp, bak[id] = x;
	if(son[x]) pre_tree_edge(son[x], tp);
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(y != son[x]) pre_tree_edge(y, y);
	}
}

void pre_tree_node_again(int x) {
	s.change(ll[x], 1);
	tot[x] = 1;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(!g[y]) {
			pre_tree_node_again(y);
			tot[x] += tot[y];
			if(tot[y] > tot[Son[x]]) Son[x] = y;
		}
	} if(Son[x]) ans += Son[x];
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(!g[y]) {
			if(Son[x] == y) t.change(1, 1, n, ys[y], 0);
			else t.change(1, 1, n, ys[y], 1);
		}
	}
}

void solve(int x) {
	int u = s.getsum(rr[fa[x]]) - s.getsum(ll[fa[x]] - 1);
	if(u == 2) ans += x, t.change(1, 1, n, ys[x], 0);
	tp = 0;
	while(x) {
		int tx = top[x];
		t.get(1, 1, n, ys[tx], ys[x]);
		x = fa[tx];
	} for(int i = 1; i <= tp; i++) {
		x = bak[sta[i]];
		if(x == 1) break;
		int f = fa[x];
		if(L[f] == x) {
			if(R[f]) {
				int c1 = s.getsum(rr[x]) - s.getsum(ll[x] - 1);
				int c2 = s.getsum(rr[R[f]]) - s.getsum(ll[R[f]] - 1);
				if(c1 > c2) t.change(1, 1, n, ys[x], 0), t.change(1, 1, n, ys[R[f]], 1), ans -= R[f], ans += x;
				else if(c1 == c2) {
					int h1 = t2.get(1, 1, n, ll[x], rr[x]), h2 = t2.get(1, 1, n, ll[R[f]], rr[R[f]]);
					if(h1 >= h2) t.change(1, 1, n, ys[x], 0), t.change(1, 1, n, ys[R[f]], 1), ans -= R[f], ans += x;
				}
			}
		} else {
			if(L[f]) {
				int c1 = s.getsum(rr[x]) - s.getsum(ll[x] - 1);
				int c2 = s.getsum(rr[L[f]]) - s.getsum(ll[L[f]] - 1);
				if(c1 > c2) t.change(1, 1, n, ys[x], 0), t.change(1, 1, n, ys[L[f]], 1), ans -= L[f], ans += x;
				else if(c1 == c2) {
					int h1 = t2.get(1, 1, n, ll[x], rr[x]), h2 = t2.get(1, 1, n, ll[L[f]], rr[L[f]]);
					if(h1 > h2) t.change(1, 1, n, ys[x], 0), t.change(1, 1, n, ys[L[f]], 1), ans -= L[f], ans += x;
				}
			}
		}
	}
}

int main() {
	n = read();
	for(int i = 1; i <= n; i++) {
		L[i] = read(), R[i] = read();
		if(R[i]) ins(i, R[i]);
		if(L[i]) ins(i, L[i]);
		g[i] = 0;
	} int m = read();
	for(int i = 1; i <= m; i++) q[i] = read(), g[q[i]] = i;
	pre_tree_node(1), id = 0, pre_tree_edge(1, 1);
	t.bt(1, n), t2.bt(1, n);
	memset(tot, 0, sizeof(tot));
	pre_tree_node_again(1);
	for(int i = 1; i <= n; i++) {
		if(g[i]) t.change(1, 1, n, ys[i], 1);
		t2.change(1, 1, n, ll[i], g[i]);
	}
	pp[++zz] = ans;
	for(int i = m; i >= 1; i--) {
		int x = q[i];
		t2.change(1, 1, n, ll[x], 0);
		s.change(ll[x], 1);
		solve(x);
		pp[++zz] = ans;
	} for(int i = zz; i >= 1; i--) put(pp[i]), puts("");
	return 0;
}


反质数序列

你不管 1 1 1的情况,留着特判。
因为一个质数肯定是由奇数和偶数组成的,所以这是个二分图的最大独立集。


#include <ctime>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int C = 200001, N = 5002;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

struct edge {
	int x, y, c, next;
} e[N * N]; int len, last[N];
int st, ed, h[N];
int plen, p[C];
queue<int> q;
bool v[C];
int a[N];

void ins(int x, int y, int c) {
	e[++len].x = x, e[len].y = y, e[len].c = c;
	e[len].next = last[x], last[x] = len;
	e[++len].x = y, e[len].y = x, e[len].c = 0;
	e[len].next = last[y], last[y] = len;
}

void get_p() {
	for(int i = 2; i < C; i++) {
		if(!v[i]) p[++plen] = i;
		for(int j = 1; j <= plen && (LL)i * p[j] < C; j++) {
			v[i * p[j]] = 1;
			if(i % p[j] == 0) break;
		}
	}
}

bool bfs() {
	memset(h, 0, sizeof(h)); h[st] = 1;
	q.push(st);
	while(!q.empty()) {
		int x = q.front(); q.pop();
		for(int k = last[x]; k; k = e[k].next) {
			int y = e[k].y;
			if(!h[y] && e[k].c) {
				h[y] = h[x] + 1;
				q.push(y);
			}
		}
	} if(!h[ed]) return 0;
	return 1;
}

int dfs(int x, int flow) {
	if(x == ed) return flow;
	int tt = 0, minf;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(h[y] == h[x] + 1 && e[k].c && tt < flow) {
			minf = dfs(y, _min(e[k].c, flow - tt));
			e[k].c -= minf, e[k ^ 1].c += minf;
			tt += minf;
		}
	} if(!tt) h[x] = 0;
	return tt;
}

int main() {
	get_p();
	int n = read(); bool one = 0;
	for(int i = 1; i <= n; i++) a[i] = read(), one |= a[i] == 1;
	len = 1; st = 0, ed = n + 1; int yy = 0;
	for(int i = 1; i <= n; i++) if(a[i] != 1){
		yy++;
		if(a[i] % 2 == 0) ins(st, i, 1);
		else ins(i, ed, 1);
		for(int j = i + 1; j <= n; j++) if(a[j] != 1){
			if(!v[a[i] + a[j]]) {
				if(a[i] % 2 == 0) ins(i, j, 1);
				else ins(j, i, 1);
			}
		}
	} int ans = 0;
	while(bfs()) 
	ans += dfs(st, 999999999);
	ans = yy - ans;
	if(one) {
		len = 1; memset(last, 0, sizeof(last));
		yy = 0;
		for(int i = 1; i <= n; i++) if(a[i] != 1 && v[a[i] + 1]){
			yy++;
			if(a[i] % 2 == 0) ins(st, i, 1);
			else ins(i, ed, 1);
			for(int j = 1; j <= i + 1; j++) if(a[j] != 1 && v[a[j] + 1]){
				if(!v[a[i] + a[j]]) {
					if(a[i] % 2 == 0) ins(i, j, 1);
					else ins(j, i, 1);
				}
			}
		} int sum = 0;
		while(bfs()) sum += dfs(st, 999999999);
		ans = _max(ans, yy - sum + 1);
	} put(ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值