部分互测题,专项测试题题解

互测部分

1

https://www.cnblogs.com/Chencgy/p/16970117.html

2

A. 营救皮卡丘

跑弗洛伊德,搞出i>j 不经过比 i,j 编号大的点的最小花费

每个点都要走一遍,套路的拆点补流

k 的流量直接从源点到 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 = 555;
const int inf = 0x3f3f3f3f;
int n, m, K, f[maxn][maxn];
struct MCMF{
	struct edge{int to, net, val, cost;}e[maxn * maxn];
	int head[maxn], dis[maxn], tot = 1, s, t;
	bool vis[maxn];
	queue<int>q;
	void add(int u, int v, int w, int c){
		e[++tot].net = head[u];
		head[u] = tot;
		e[tot].to = v;
		e[tot].val = w;
		e[tot].cost = c;
	}
	void link(int u, int v, int w, int c){add(u, v, w, c); add(v, u, 0, -c);}
	bool spfa(){
		for(int i = 1; i <= t; ++i)dis[i] = inf, vis[i] = false;
		q.push(s); dis[s] = 0;
		while(!q.empty()){
			int x = q.front(); q.pop(); vis[x] = false;
			for(int i = head[x]; i; i = e[i].net){
				int v = e[i].to;
				if(e[i].val > 0 && dis[v] > dis[x] + e[i].cost){
					dis[v] = dis[x] + e[i].cost;
					if(!vis[v])q.push(v), vis[v] = true;
				}
			}
		}
		return dis[t] != inf;
	}
	int dfs(int x, int from){
		if(x == t || from <= 0)return from;
		vis[x] = true; int res = from;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(!vis[v] && e[i].val > 0 && dis[v] == dis[x] + e[i].cost){
				int k = dfs(v, min(res, e[i].val));
				e[i].val -= k;
				e[i ^ 1].val += k;
				res -= k;
				if(res <= 0)break;
			}
		}
		return from - res;
	}
	int flow, cost;
	void mcmf(){
		while(spfa()){
			int k = dfs(s, inf);
			flow += k;
			cost += k * dis[t];
		}
	}
	void init(){
		n = read(), m = read(), K = read(); ++n;
		for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)f[i][j] = inf;
		for(int i = 1; i <= m; ++i){
			int u = read() + 1; int v = read() + 1;
			f[u][v] = f[v][u] = min(read(), f[u][v]);
		}
		for(int i = 1; i <= n; ++i)f[i][i] = 0;
		for(int k = 1; k <= n; ++k)
			for(int i = 1; i <= n; ++i)
				for(int j = 1; j <= n; ++j)
					if(k < i || k < j)f[i][j] = min(f[i][k] + f[k][j], f[i][j]);
		for(int i = 1; i <= n; ++i)
			for(int j = i + 1; j <= n; ++j)
				if(f[i][j] != inf)link(i, j + n, inf, f[i][j]);
		s = n + n + 1, t = s + 1;
		link(s, 1, K, 0);
		for(int i = 1; i <= n; ++i){
			link(i + n, t, 1, 0);
			if(i != 1)link(s, i, 1, 0);
		}
		mcmf();
		printf("%d\n",cost);

	}
}W;

int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	W.init();
	return 0;
}

B. Snack

直接建图非常显然,然后考虑模拟最小割的过程优化复杂度

钦定零食连 S 还是连 T

则对于每个小孩,连 S 的代价为 ci

T 的代价为 xbi​,

其中 x 为零食数量。

那么枚举 x, 贪心的选最小的 nx 个割掉

小孩的贡献就是 min(ci,xbi),这个可以按 ci/bi​​ 排序后双指针维护。

code
#include<cstdio>
#include<cstring>
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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

const int maxn = 300005;
ll n, m, a[maxn];
struct node{
	ll b, c;
	bool operator < (const node &r)const{
		return c * r.b > r.c * b;
	}
}d[maxn];

int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= m; ++i)d[i].b = read();
	ll sc = 0;
	for(int i = 1; i <= m; ++i)d[i].c = read(), sc += d[i].c;
	sort(d + 1, d + m + 1); sort(a + 1, a + n + 1);
	ll sa = 0, sb = 0, ans = LLONG_MAX;
	for(int i = n, j = 1; i >= 0; --i){
		sa += a[n - i];
		for(; j <= m && d[j].b * i <= d[j].c; ++j)sb += d[j].b, sc -= d[j].c;
		ans = min(ans, sa + sb * i + sc);
	}
	printf("%lld\n",ans);
	return 0;
}

C. minimization

绝对值这玩意很难受,考虑在值域上建图,在相同值之间建边来得到代价

如果把数轴画出来,建图就容易多了

每个数的选择都建一条数轴,选择的数前面建流量为选择代价的边

把数轴上数值相同的点都连起来,流量为 wi,j

然后考虑优化点数,建了很多无用点,实际有用的只有 a 取值的若干点

把中间的点去掉,与其他数值的流量都放到a 那里连

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

using namespace std;
typedef long long ll;
#define int long long
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = (x * 10) + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 75;
const int maxm = 5e5 + 55;
const int inf = 1e18;
int n, m;
int a[maxn][maxn], c[maxn][maxn], id[maxn][maxn];

struct Dinic{
	int s, t, cnt;
	int head[maxm], tot = 1;
	struct edge{int to, net; ll 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){
		if(w)printf("%d %d %d\n",u, v, w);	
		add(u, v, w); add(v, u, 0);
	}
	int dep[maxm], now[maxm];
	bool bfs(){
		memset(dep, 0, sizeof(dep));
		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; 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){
				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;
			}
		}
		now[x] = i;
		return from - res;
	}
	ll dinic(){
		ll ans = 0;
		while(bfs())ans += dfs(s, inf);
		return ans;
	}
	void init(){
		n = read(), m = read();
		s = 1, t = 2, cnt = 2;
		for(int i = 1; i <= n; ++i){
			id[i][0] = s; id[i][m] = t;
			for(int j = 1; j < m; ++j)id[i][j] = ++cnt;
			for(int j = 1; j <= m; ++j){
				a[i][j] = read(), c[i][j] = read();
				link(id[i][j - 1], id[i][j], c[i][j]);
			}
			a[i][m + 1] = inf;
		}
		for(int i = 1; i <= n; ++i)
			for(int j = i + 1; j <= n; ++j){
				int w = read();
				for(int x = 1; x <= m; ++x)
					for(int y = 1; y <= m; ++y){
						link(id[i][x - 1], id[j][y], max(0ll, min(a[i][x], a[j][y + 1]) - max(a[i][x - 1], a[j][y])) * w);
						link(id[j][x - 1], id[i][y], max(0ll, min(a[j][x], a[i][y + 1]) - max(a[j][x - 1], a[i][y])) * w);
					}
			}
		printf("%lld\n",dinic());
	}
}D;

signed main(){
	// freopen("minimization.in","r",stdin);
	// freopen("minimization.out","w",stdout);
	D.init();
	return 0;
}

4

A. Manong

非常码农,不想搞

code

B. 字符串

就是对两个字符串集合跑最大匹配

考虑如何在 SAM 上做

因为两个串的 SAM 上的 lca 就是他们的 lcs

所以贪心的在深度深的位置匹配

即能匹配就匹配就是最优的

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

using namespace std;
typedef long long ll;
const int maxn = 1e6 + 55;
char a[maxn], b[maxn];
int n, k;
struct node{
	int ch[26], len, fa;
}d[maxn * 5];
struct SAM{
	int cnt = 1, las = 1;
	int count[maxn * 5][2];
	void ins(int c, int is, int typ){
		int fa = las, now = las = ++cnt;
		d[now].len = d[fa].len + 1;
		count[now][typ] += is;
		for(; fa && !d[fa].ch[c]; fa = d[fa].fa) d[fa].ch[c] = now;
		if(!fa)d[now].fa = 1;
		else{
			int j = d[fa].ch[c];
			if(d[j].len == d[fa].len + 1)d[now].fa = j;
			else{
				int clone = ++cnt;
				d[clone] = d[j];
				d[clone].len = d[fa].len + 1;
				d[j].fa = d[now].fa = clone;
				for(; fa && d[fa].ch[c] == j; fa = d[fa].fa)d[fa].ch[c] = clone;
			}
		}
	}
	int t[maxn], rk[maxn];
	void tsp(){
		for(int i = 1; i <= cnt; ++i)++t[d[i].len];
		for(int i = 1; i <= cnt; ++i)t[i] += t[i - 1];
		for(int i = 1; i <= cnt; ++i)rk[t[d[i].len]--] = i;
	}
	ll solve(){
		ll ans=0;
		for(int i = cnt; i >= 1; --i){
			ll mi = min(count[rk[i]][0],count[rk[i]][1]);
			ans += mi * min(d[rk[i]].len, k);
			count[rk[i]][0] -= mi; count[rk[i]][1] -= mi;
			count[d[rk[i]].fa][0] += count[rk[i]][0];
			count[d[rk[i]].fa][1] += count[rk[i]][1];
		}
		return ans;
	}
}S;

int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d",&n,&k);
	scanf("%s%s",a + 1,b + 1);
	for(int i = n; i >= 1; --i)S.ins(a[i] - 'a', (i + k - 1 <= n), 0);
	for(int i = n; i >= 1; --i)S.ins(b[i] - 'a', (i + k - 1 <= n), 1);
	S.tsp();
	printf("%lld\n",1ll * k * (n - k + 1) - S.solve());
	return 0;
}

C. sstr

动态开点线段树维护每个结点的 endpos 集合

然后每次先跑匹配,倒着贪心

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

using namespace std;

typedef long long ll;
const int maxn = 5e5 + 55;

int n, q, m, l, r, top;
struct node{
	int fa, len, ch[26];
}d[maxn];
int root[maxn];
struct seg{
	int cnt, ls[maxn * 40], rs[maxn * 40];
	void ins(int &x, int l, int r, int pos){
		if(!x)x = ++cnt;
		if(l == r)return;
		int mid = (l + r) >> 1;
		if(pos <= mid)ins(ls[x], l, mid, pos);
		else ins(rs[x], mid + 1, r, pos);
	}
	int merge(int x, int y, int l, int r){
		if(!x || !y)return x | y;
		int now = ++cnt;
		if(l == r)return now;
		int mid = (l + r) >> 1;
		ls[now] = merge(ls[x], ls[y], l, mid);
		rs[now] = merge(rs[x], rs[y], mid + 1, r);
		return now;
	}
	int query(int x, int l, int r, int L, int R){
		if(!x)return 0;
		if(L <= l && r <= R)return r - l + 1;
		int mid = (l + r) >> 1;
		if(L <= mid && R > mid)return query(ls[x], l, mid, L, R) + query(rs[x], mid + 1, r, L, R);
		if(L <= mid) return query(ls[x], l, mid, L, R);
		else return query(rs[x], mid + 1, r, L, R);
	}
}T;
struct SAM{
	int cnt = 1, las = 1, pos[maxn];
	void ins(int c,int id){
		int fa = las, now = las = ++cnt;
		d[now].len = d[fa].len + 1;
		pos[now] = id; T.ins(root[now], 1, n, id);
		for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
		if(!fa)d[now].fa = 1;
		else{
			int j = d[fa].ch[c];
			if(d[j].len == d[fa].len + 1)d[now].fa = j;
			else{
				int clone = ++cnt; d[clone] = d[j];
				d[clone].len = d[fa].len + 1;
				d[now].fa = d[j].fa = clone;
				pos[clone] = id;
				for(; fa && d[fa].ch[c] == j; fa = d[fa].fa)d[fa].ch[c] = clone;
			}
		}
	}
	vector<int>g[maxn];
	void dfs(int x){
		for(int v : g[x]){
			dfs(v);
			root[x] = T.merge(root[x], root[v], 1, n);
		}
	}
	void built(){
		for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);
		dfs(1);
	}
}S;
char s[maxn], c[maxn], p[maxn];

bool dfs(int x, int len){
	if(len == m){
		for(int i = 0; i < 26; ++i)if(d[x].ch[i] && T.query(root[d[x].ch[i]], 1, n, l + len, r)){
			p[++top] = i + 'a';
			return true;
		}
		return false;
	}
	int val = c[len + 1] - 'a';
	if(d[x].ch[val] && T.query(root[d[x].ch[val]], 1, n, l + len, r) && dfs(d[x].ch[val], len + 1)){
		p[++top] = c[len + 1]; return true;
	}
	for(int i = val + 1; i < 26; ++i)if(d[x].ch[i] && T.query(root[d[x].ch[i]], 1, n, l + len, r)){
		p[++top] = i + 'a';
		return true;
	}
	return false;
}

void solve(){
	m = strlen(c + 1);
	top = 0;
	if(dfs(1, 0)){
		for(int i = top; i >= 1; --i)putchar(p[i]);putchar('\n');
	}else printf("-1\n");
}
int main(){
	// freopen("sstr.in","r",stdin);
	// freopen("sstr.ans","w",stdout);
	scanf("%s",s + 1);
	n = strlen(s + 1);
	for(int i = 1; i <= n; ++i)S.ins(s[i] - 'a', i);
	S.built();
	scanf("%d",&q);
	cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
	for(int i = 1; i <= q; ++i){
		scanf("%d%d%s",&l, &r, c + 1);
		solve();
	cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;

	}
	return 0;
}

5

真的找不到题了,考的题我找题时候做过俩。。。

A. Light

枚举 A 的长度 L, 每 L 个位置设一个关键点

两个 A 必然都过关键点

从一个关键点 +L+g 可以得到对应的匹配的位置

找到两者的 LCP+LCS 确定范围

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, g;
char c[maxn];

struct SA{
	int s[maxn];
	int ht[maxn];
	int sa[maxn], id[maxn], px[maxn], cnt[maxn], rk[maxn << 1], ork[maxn << 1 | 1];
	bool cmp(int x, int y, int w){return ork[x] == ork[y] && ork[x + w] == ork[y + w];}
	int st[maxn][19];
	int n;
	void clear(){
		for(int i = 1; i <= n; ++i)sa[i] = rk[i] = ht[i] = 0;
		for(int i = 0; (1 << i) <= n; ++i)
			for(int j = 1; j + (1 << i) - 1 <= n; ++j)
				st[j][i] = 0;	
	}
	void get_sa(){
		int m = max(n, 300);
		for(int i = 1; i <= m; ++i)cnt[i] = 0;
		for(int i = 1; i <= n; ++i)++cnt[rk[i] = s[i]];
		for(int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1];
		for(int i = n; i >= 1; --i)sa[cnt[rk[i]]--] = i;
		for(int w = 1, p; w <= n; w <<= 1){
			p = 0;
			for(int i = n; i > n - w; --i)id[++p] = i;
			for(int i = 1; i <= n; ++i)if(sa[i] > w)id[++p] = sa[i] - w;
			for(int i = 1; i <= m; ++i)cnt[i] = 0;
			for(int i = 1; i <= n; ++i)++cnt[px[i] = rk[id[i]]];
			for(int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1];
			for(int i = n; i >= 1; --i)sa[cnt[px[i]]--] = id[i];
			for(int i = 1; i <= n; ++i)ork[i] = rk[i];
			p = 0;
			for(int i = 1; i <= n; ++i)rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
			if(p == n)break;
			m = p;
		}
	}
	void get_ht(){
		for(int i = 1, k = 0; i <= n; ++i){
			if(rk[i] == 1)continue;
			if(k)--k;
			while(s[i + k] == s[sa[rk[i] - 1] + k])++k;
			ht[rk[i]] = k;
		}
	}
	void get_st(){
		for(int i = 1; i <= n; ++i)st[i][0] = ht[i];
		for(int j = 1; (1 << j) <= n; ++j)
			for(int i = 1; i + (1 << j) - 1 <= n; ++i)
				st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
	}
	void init(){
		get_sa();
		get_ht();
		get_st();
	}
	int query(int l, int r){
		if(l == r)return n - sa[l] + 1;
		if(l > r)swap(l, r); ++l;
		int k = log2(r - l + 1);
		return min(st[l][k], st[r - (1 << k) + 1][k]);
	}
}LCP, LCS;

void sol(){
	scanf("%d%s",&g, c + 1);
	n = strlen(c + 1);
	LCP.clear(); LCS.clear();
	LCP.n = LCS.n = n;
	for(int i = 1; i <= n; ++i)LCP.s[i] = LCS.s[n - i + 1] = c[i] - 'a' + 1;
	LCP.init(); LCS.init();
	int ans = 0;
	for(int len = 1; len <= n; ++len){
		for(int i = 1; i <= n; i += len){
			int l = i, r = i + len + g;
			if(r > n)break;
			int R = n - l + 1, L = n - r + 1;
			int S = min(len, LCS.query(LCS.rk[L], LCS.rk[R]));
			int P = min(len, LCP.query(LCP.rk[l], LCP.rk[r]));
			int res = S + P - len + (S == 0 || P == 0);
			ans = ans + max(0, res);
		}
	}
	printf("%d\n",ans);
}


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

B. 发烧

启发式合并维护当前节点在哪些串出现过,然后直接跑匹配

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

using namespace std;

typedef long long ll;

const int maxn = 500005;

struct node{
	int fa, len, ch[26];
}d[maxn];
int n, k;
string s[maxn];
set<int>st[maxn];
vector<int>g[maxn];
int num[maxn];
struct SAM{
	int las = 1, cnt = 1;
	void ins(int c, int pos){
		if(d[las].ch[c]){
			int fa = las, x = d[las].ch[c];
			if(d[fa].len + 1 == d[x].len)las = x;
			else{
				int now = las = ++cnt;
				d[now] = d[x];
				d[now].len = d[fa].len + 1;
				d[x].fa = now;
				for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = now;
			}
		}else{
			int fa = las, now = las = ++cnt; d[now].len = d[fa].len + 1;
			for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
			if(!fa)d[now].fa = 1;
			else{
				int x = d[fa].ch[c];
				if(d[fa].len + 1 == d[x].len)d[now].fa = x;
				else{
					int clone = ++cnt;
					d[clone] = d[x];
					d[clone].len = d[fa].len + 1;
					d[now].fa = d[x].fa = clone;
					for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
				}
			}
		}
		st[las].insert(pos);
	}
	void merge(set<int>&x, set<int>&y){
		if(x.size() < y.size())swap(x, y);
		for(int v : y)x.insert(v);
		y.clear();
	}
	void dfs(int x){
		for(int v : g[x]){
			dfs(v);
			merge(st[x], st[v]);
		}
		num[x] = st[x].size();
	}
	void built(){
		for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);
		dfs(1);
	}
}S;
ll sol(int id){
	int len = s[id].size(), now = 1;
	ll ans = 0;
	for(int i = 0; i < len; ++i){
		int c = s[id][i] - 'a';
		while(now != 1 && !d[now].ch[c])now = d[now].fa;
		if(d[now].ch[c])now = d[now].ch[c];
		while(now != 1 && num[now] < k)now = d[now].fa;
		if(num[now] >= k)ans += d[now].len;
	}
	return ans;
}
int main(){
	freopen("fever.in","r",stdin);
	freopen("fever.out","w",stdout);
	cin >> n >> k;
	for(int i = 1; i <= n; ++i){
		cin >> s[i];
		int len = s[i].size();
		S.las = 1;
		for(int j = 0; j < len; ++j)S.ins(s[i][j] - 'a', i);
	}
	S.built();
	for(int i = 1; i <= n; ++i)cout << sol(i) << " ";cout << endl;
	return 0;;
}

C. Big_Water_Problem

建广义 SAM, 分别维护两个串的 endpos 集合大小

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

using namespace std;

typedef long long ll;

const int maxn = 500005;

struct node{
	int fa, len, ch[26];
}d[maxn];
char s[maxn], c[maxn];
int n, m;
vector<int>g[maxn];
struct SAM{
	int si[maxn][2];
	int las = 1, cnt = 1;
	void ins(int c, int id){
		if(d[las].ch[c]){
			int fa = las, x = d[las].ch[c];
			if(d[fa].len + 1 == d[x].len)las = x;
			else{
				int now = las = ++cnt;
				d[now] = d[x];
				d[now].len = d[fa].len + 1;
				d[x].fa = now;
				for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = now;
			}
		}else{
			int fa = las, now = las = ++cnt; d[now].len = d[fa].len + 1;
			for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
			if(!fa)d[now].fa = 1;
			else{
				int x = d[fa].ch[c];
				if(d[fa].len + 1 == d[x].len)d[now].fa = x;
				else{
					int clone = ++cnt;
					d[clone] = d[x];
					d[clone].len = d[fa].len + 1;
					d[now].fa = d[x].fa = clone;
					for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
				}
			}
		}
		++si[las][id];
	}
	void dfs(int x){
		for(int v : g[x]){
			dfs(v);
			si[x][0] += si[v][0];
			si[x][1] += si[v][1];
		}
	}
	int solve(){
		for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);
		dfs(1);
		int mi = 0x3f3f3f3f;
		for(int i = 2; i <= cnt; ++i)if(si[i][0] == 1 && si[i][1] == 1)mi = min(mi, d[d[i].fa].len + 1);
		return mi == 0x3f3f3f3f ? -1 : mi;
	}
}S;

int main(){
	freopen("Big_Water_Problem.in","r",stdin);
	freopen("Big_Water_Problem.out","w",stdout);
	scanf("%s%s",s + 1, c + 1);
	n = strlen(s + 1); m = strlen(c + 1);
	for(int i = 1; i <= n; ++i)S.ins(s[i] - 'a', 0);
	S.las = 1; 
	for(int i = 1; i <= m; ++i)S.ins(c[i] - 'a', 1);
	printf("%d\n",S.solve());
	return 0;;
}

6

CF755G / 小组学习

fi,j 为考虑前 i 个球,取了 j 组的方案数

显然有 fi,j=fi1,j+fi1,j1+fi2,j1

如果把当前分组拆成两部分,分拼接部分是一组还是两组考虑

fi,j=fk,pfik,jp+fk1,pfik1,jp1

fa+b,k=i=0kfa,ifb,ki+i=0k1fa1,ifb1,ki1

那么考虑倍增

为了方便先写成多项式 Fn(x) 为了省事下面不写 (x)

F2n=Fn2+Fn12

F2n1=FnFn1+xFn1Fn2

F2n2=Fn12+Fn22

所以可以搞了,具体来讲就是按照二进制位考虑,每次左移或者加一

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 131075;
const int mod = 998244353;

int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int deg, len, rev[maxn], wn[maxn];
void init(){
	deg = 1; while(deg < len)deg <<= 1;
	wn[0] = 1; wn[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);
		wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
	}
}
struct poly{
	int f[maxn];
	int &operator [] (const int &i){return f[i];}
	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){
				int w = 1;
				for(int j = i; j < i + hl; ++j){
					int x = f[j], y = 1ll * w * f[j + hl] % mod;
					f[j] = (x + y) % mod; f[j + hl] = (0ll + x - y + mod) % mod;
					w = 1ll * w * wn[deg / l] % mod;
				}
			}
		}
	}
	void intt(){
		ntt(); 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;
	}
}f[3], g[5];
int n, k;
void mul(){
	f[0].ntt(); f[1].ntt(); f[2].ntt();
	for(int i = 0; i < deg; ++i)g[0][i] = g[1][i] = g[2][i] = g[3][i] = g[4][i] = 0;
	for(int i = 0; i < deg; ++i){
		g[0][i] = 1ll * f[0][i] * f[0][i] % mod;
		g[1][i] = 1ll * f[1][i] * f[1][i] % mod;
		g[2][i] = 1ll * f[2][i] * f[2][i] % mod;
		g[3][i] = 1ll * f[0][i] * f[1][i] % mod;
		g[4][i] = 1ll * f[1][i] * f[2][i] % mod;
	}
	g[0].intt(); g[1].intt(); g[2].intt(); g[3].intt(); g[4].intt();
	for(int i = 0; i <= k; ++i){
		f[0][i] = (g[0][i] + (i ? g[1][i - 1] : 0)) % mod;
		f[1][i] = (g[3][i] + (i ? g[4][i - 1] : 0)) % mod;
		f[2][i] = (g[1][i] + (i ? g[2][i - 1] : 0)) % mod;
	}
	for(int i = k + 1; i < deg; ++i)f[0][i] = f[1][i] = f[2][i] = 0;
}
void add(){
	for(int i = 0; i <= k; ++i)f[2][i] = f[1][i], f[1][i] = f[0][i]; f[0][0] = 1;
	for(int i = 1; i <= k; ++i)f[0][i] = (0ll + f[1][i] + f[1][i - 1] + f[2][i - 1]) % mod;
}
int main(){
	// freopen("group.in","r",stdin);
	// freopen("group.ans","w",stdout);
	scanf("%d%d",&n,&k); len = k + k + 5;
	f[0][0] = f[0][1] = f[1][0] = 1; init();
	for(int i = log2(n) - 1; i >= 0; --i){mul();if(n & (1 << i))add();}
	for(int i = 1; i <= k; ++i)printf("%d ",f[0][i]);
	return 0;
}

CF623E Transforming Sequence

fi,j 表示前 i 个数,或值有 j 位为 1, (只知道他们的相对大小关系,最后乘上组合数即可)

fi,j=k=0jfi1,k(jk)2k

可得

fn+m,i=j=0ifn,jfm,ij(ij)2mj

然后可以倍增,过程中维护 fi,j/(j!) 方便处理

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 131075;
const int mod = 998244353;

int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int deg, len, rev[maxn], wn[maxn];
void init(){
	deg = 1; while(deg < len)deg <<= 1;
	wn[0] = 1; wn[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);
		wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
	}
}
struct poly{
	int f[maxn];
	int &operator [] (const int &i){return f[i];}
	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){
				int w = 1;
				for(int j = i; j < i + hl; ++j){
					int x = f[j], y = 1ll * w * f[j + hl] % mod;
					f[j] = (x + y) % mod; f[j + hl] = (x - y + mod) % mod;
					w = 1ll * w * wn[deg / l] % mod;
				}
			}
		}
	}
	void intt(){
		ntt(); 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;
	}
}f, g;
int k;
int fac[maxn], inv[maxn];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
void solve(int n){
	if(n == 1){
		f[0] = 0; for(int i = 1; i <= k; ++i)f[i] = inv[i];
		return;
	}
	solve(n >> 1);
	g[0] = 0; for(int i = 1; i <= k; ++i)g[i] = 1ll * f[i] * qpow(2, 1ll * (n >> 1) * i % (mod - 1)) % mod;
	for(int i = k + 1; i < deg; ++i)f[i] = g[i] = 0;
	f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod; f.intt();
	if(n & 1){
		g[0] = 0;
		for(int i = 1; i <= k; ++i)f[i] = 1ll * f[i] * qpow(2, i) % mod, g[i] = inv[i];
		for(int i = k + 1; i < deg; ++i)f[i] = g[i] = 0;
		f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod; f.intt();
	}
}
ll n;
int main(){
	// freopen("ts.in","r",stdin);
	// freopen("ts.ans","w",stdout);
	scanf("%lld%d",&n,&k);
	if(n > k){printf("0\n");return 0;}
	fac[0] = inv[0] = 1; for(int i = 1; i <= k; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[k] = qpow(fac[k], mod - 2); for(int i = k - 1; i >= 1; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
	len = k + k + 10; init();
	solve(n);
	int ans = 0;
	for(int i = 1; i <= k; ++i)ans = (ans + 1ll * f[i] * fac[k] % mod * inv[k - i] % mod ) % mod;
	printf("%d\n",ans);
	return 0;
}

fi,x 表示考虑 i 的子树,选择了 i 异或为 j 的方案数

转移发现就是异或卷积卷上fson

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

using namespace std;
int read(){
	int x = 0; bool f = 0; 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 = 2505;
const int mod = 1e9 + 7;
const int inv2 = (mod + 1) / 2;
int n, m, v[maxn], fa[maxn];
void fwt_xor(int f[], int opt){
	for(int l = 2, k = 1; l <= m; l <<= 1, k <<= 1)
		for(int i = 0; i < m; i += l)
			for(int j = 0; j < k; ++j){
				int x = f[i + j], y = f[i + j + k];
				f[i + j] = 1ll * opt * (x + y) % mod;
				f[i + j + k] = 1ll * opt * (x - y + mod) % mod;
			}
}

vector<int>g[maxn];
int f[maxn][maxn], ans[maxn];
void dfs(int x){
	f[x][v[x]] = 1; fwt_xor(f[x], 1);
	for(int v : g[x])if(v != fa[x]){
		fa[v] = x; dfs(v);
		for(int i = 0; i < m; ++i)f[x][i] = 1ll * f[x][i] * f[v][i] % mod;
	}
	fwt_xor(f[x], inv2);
	for(int i = 0; i < m; ++i)ans[i] = (ans[i] + f[x][i]) % mod;
	++f[x][0]; fwt_xor(f[x], 1);
}
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)v[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);
	for(int i = 0; i < m; ++i)printf("%d ",ans[i]);
	return 0;
}

7

A. tree / 无聊的水题 I

pufer 序列与有标号无根树一一对应

问题变成求多少 pufer 序列中有元素出现 m1 次,并且没有出现次数更多的

转化成至多 m1 次 - 至多 m2

假设我们知道每个数出现多少次,那么答案就是 (n2)!Πcnti

于是设指数型生成函数 F=i=0m1i

[xn1]Fn 即为所求

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 400005;
const int mod = 998244353;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int rev[maxn], wn[maxn], deg;
void init(int len){
	deg = 1; while(deg < len)deg <<= 1;
	wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
	for(int i = 1; i < deg; ++i){
		wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
		rev[i] = rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= (deg >> 1);
	}
}
int add(int x, int y){x += y; return x >= mod ? x - mod : x;}
struct poly{
	int f[maxn];
	int &operator [](const int &i){return f[i];}
	int operator [](const int &i)const{return f[i];}
	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){
				int w = 1, x, y;
				for(int j = i; j < i + hl; ++j){
					x = f[j], y = 1ll * w * f[j + hl] % mod; 
					f[j] = add(x, y); f[j + hl] = add(x, mod - y);
					w = 1ll * w * wn[deg / l] % mod;
				}
			}
	}
	void intt(){
		ntt(); 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;
	}
	void del(int l){for(int i = l; i < deg; ++i)f[i] = 0;}
	void operator *= (const poly x){
		ntt(); poly a = x; a.ntt();
		for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * a[i] % mod;
		intt();
	}
}f, g;
int n, m, fac[maxn], ifac[maxn];
int sol(int mx){
	init(n + n);
	for(int i = 0; i <= mx; ++i)g[i] = ifac[i];
	for(int i = mx + 1; i < deg; ++i)g[i] = 0;
	for(int i = 1; i < deg; ++i)f[i] = 0; f[0] = 1;
	int y = n; for(; y; y >>= 1, g *= g, g.del(n - 1))if(y & 1)f *= g, f.del(n - 1);
	return 1ll * f[n - 2] * fac[n - 2] % mod;
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.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;
	printf("%d\n",(sol(m - 1) - sol(m - 2) + mod) % mod);
	return 0;
}

B. 残缺的字符串

反转一个,匹配变成卷积形式

如果字符集小,可以枚举字符设成 1 ,直接乘

但是 26 的常数足够 T 飞了

f(x)g(y) 换掉,变成 f(x)g(y)(f(x)g(y))2

把通配符设成 0

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 2000005;
const int mod = 998244353;
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int rev[maxn], wn[maxn], deg;
void init(int len){
	deg = 1; while(deg < len)deg <<= 1;
	wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
	for(int i = 1; i < deg; ++i){
		wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
		rev[i] = rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= (deg >> 1);
	}
}
int add(int x, int y){x += y; return x >= mod ? x - mod : x;}
struct poly{
	int f[maxn];
	int &operator [](const int &i){return f[i];}
	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){
				int w = 1, x, y;
				for(int j = i; j < i + hl; ++j){
					x = f[j], y = 1ll * w * f[j + hl] % mod; 
					f[j] = add(x, y); f[j + hl] = add(x, mod - y);
					w = 1ll * w * wn[deg / l] % mod;
				}
			}
	}
	void intt(){
		ntt(); 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;
	}
}f, g;
int n, m, h[maxn];
char s[maxn], t[maxn];
int a[maxn], b[maxn];
void solve(char ch){
	for(int i = 0; i < deg; ++i)f[i] = (s[i] == ch || s[i] == '*');
	for(int i = 0; i < deg; ++i)g[i] = (t[i] == ch || t[i] == '*');
	f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
	f.intt();
}

int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d",&n,&m); init(n + m + 1);
	scanf("%s%s",s,t); reverse(s, s + n);
	for(int i = 0; i < n; ++i)a[i] = s[i] == '*' ? 0 : s[i] - 'a' + 1;
	for(int i = 0; i < m; ++i)b[i] = t[i] == '*' ? 0 : t[i] - 'a' + 1;
	for(int i = 0; i < deg; ++i)f[i] = a[i] * a[i] * a[i], g[i] = b[i];
	f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
	f.intt(); for(int i = 0; i < deg; ++i)h[i] = f[i];
	
	for(int i = 0; i < deg; ++i)f[i] = a[i] * a[i], g[i] = b[i] * b[i];
	f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
	f.intt(); for(int i = 0; i < deg; ++i)h[i] = (h[i] - 2ll * f[i]) % mod;

	for(int i = 0; i < deg; ++i)f[i] = a[i], g[i] = b[i] * b[i] * b[i];
	f.ntt(); g.ntt(); for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
	f.intt(); for(int i = 0; i < deg; ++i)h[i] = (h[i] + f[i]) % mod;

	int cnt = 0;
	for(int j = 0; j < m - n + 1; ++j)if(h[j + n - 1] == 0) ++cnt;
	printf("%d\n",cnt);
	if(cnt){for(int j = 0; j < m - n + 1; ++j)if(h[j + n - 1] == 0)printf("%d ",j + 1);printf("\n");}
	return 0;
}

序列 / ARC145F Modulo Sum of Increasing Sequences

咕咕咕咕

https://www.cnblogs.com/apjifengc/p/17016937.html

code

8

A. 保护出题人

前缀和出来,发现每次求

max(sisj1xi+d(ij))

可以看成 (xi+idi,si)(jdj,sj1) 的斜率

发现定点在点集的右上方,画一下图是维护下凸包

每次二分凸包即可

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 1000005;
int n, d, top;
ll a[maxn], b[maxn];
struct node{ll x, y;}st[maxn];
double slope(node x, node y){return (double)(x.y - y.y) / (double)(x.x - y.x);}
int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n = read(), d = read();
	for(int i = 1; i <= n; ++i){a[i] = read(), b[i] = read();}
	for(int i = 1; i <= n; ++i)a[i] += a[i - 1];
	double ans = 0;
	for(int i = 1; i <= n; ++i){
		node now = {1ll * i * d, a[i - 1]};
		while(top > 1 && slope(st[top], st[top - 1]) > slope(now, st[top]))--top;
		st[++top] = now; now = {b[i] + 1ll * i * d, a[i]};
		int l = 1, r = top; double res = 0;
		while(l <= r){
			if(r - l <= 5){
				for(int j = l; j <= r; ++j)
					res = max(res, slope(now, st[j]));
				break;
			}
			int mid = (l + r) >> 1;
			if(slope(now, st[mid]) > slope(now, st[mid + 1]))r = mid;
			else l = mid;
		}
		ans += res;
	}
	printf("%.0lf\n",ans);
	return 0;
}

B. 收集 / UVA1606 两亲性分子 Amphiphilic Carbon Molecules

枚举两个点确定直线,直接搞 N3

考虑确定一个点,然后按照极角排序,那么可以双指针确定选择点的范围

维护一下有多少点即可

为了方便,在处理的时候把一种颜色的点坐标取反,这样只用看一侧的点

code
// #include<bits/stdc++.h>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
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;
}
const int maxn = 1005;
struct point{int x, y; bool col;}p[maxn];
struct ver{
	double x, y, deg;
	friend bool operator < (const ver &x, const ver &y){return x.deg < y.deg;}
}d[maxn];
double cross(const ver &x, const ver &y){
	return x.x * y.y - x.y * y.x;
}
int n;
int solve(){
	if(n <= 2)return n;
	int ans = 0;
	for(int i = 1; i <= n; ++i){
		int cnt = 0;
		for(int j = 1; j <= n; ++j)if(i != j){
			d[++cnt].x = p[j].x - p[i].x;
			d[cnt].y = p[j].y - p[i].y;
			if(p[j].col)d[cnt].x = -d[cnt].x, d[cnt].y = -d[cnt].y;
			d[cnt].deg = atan2(d[cnt].y, d[cnt].x);
		}
		sort(d + 1, d + cnt + 1);
		int l = 1, r = 1, res = 2;
		while(l <= cnt){
			if(l == r)(r = r == cnt ? 1 : r + 1), ++res;
			while(l != r && cross(d[l], d[r]) >= 0){
				r = r == cnt ? 1 : r + 1;
				++res;
			}
			++l; --res;
			ans = max(ans, res);
		}
	}
	return ans;
}

int main(){
	freopen("collect.in","r",stdin);
	freopen("collect.out","w",stdout);
	n = read();
	while(n){
		for(int i = 1; i <= n; ++i)p[i].x = read(), p[i].y = read(), p[i].col = read();
		printf("%d\n",solve());
		n = read();
	}
	return 0;
}

C. 逃离包围圈 / CF932F Escape Through Leaf

发现是形如 kx+b 的函数求最值,所以李超线段树解决

因为在树上,需要子树们的信息,所以进行李超线段树合并

做法在 merge 时把一个点信息插入另外一个,再递归左右儿子

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar(); bool f = false;
	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 = 500005;
const ll inf = 1e18;
const int lim = 1e5;
int n, a[maxn], b[maxn], fa[maxn];
ll f[maxn];
vector<int>g[maxn];
struct line{ll k, b;}li[maxn];
ll calc(int id, int val){return 1ll * li[id].k * val + li[id].b;}
int tot;
int root[maxn];
struct lct{
	struct node{
		int id, l, r;
	}t[maxn << 2 | 1];
	int cnt;
	void insert(int &x, int l, int r, int id){
		if(!x){t[x = ++cnt].id = id; return;}
		int mid = (l + r) >> 1;
		ll v1 = calc(id, mid), v2 = calc(t[x].id, mid);
		if(v1 < v2)swap(id, t[x].id);
		if(l == r)return;
		if(calc(id, l) < calc(t[x].id, l))insert(t[x].l, l, mid, id);
		if(calc(id, r) < calc(t[x].id, r))insert(t[x].r, mid + 1, r, id);
	}
	int merge(int x, int y, int l, int r){
		if(!x || !y)return x | y;
		insert(x, l, r, t[y].id);
		int mid = (l + r) >> 1;
		t[x].l = merge(t[x].l, t[y].l, l, mid);
		t[x].r = merge(t[x].r, t[y].r, mid + 1, r);
		return x;
	}
	ll query(int x, int l, int r, int val){
		if(!x)return inf;
		ll ans = calc(t[x].id, val);
		if(l == r)return ans;
		int mid = (l + r) >> 1;
		if(val <= mid)ans = min(ans, query(t[x].l, l, mid, val));
		else ans = min(ans, query(t[x].r, mid + 1, r, val));
		return ans;
	}
}T;

void dfs(int x){
	bool noleaf = false;
	for(int v : g[x])if(v != fa[x]){
		fa[v] = x; noleaf = true; 
		dfs(v);
		root[x] = T.merge(root[x], root[v], -lim, lim);
	}
	if(noleaf)f[x] = T.query(root[x], -lim, lim, a[x]);
	li[++tot] = {b[x], f[x]};
	T.insert(root[x], -lim, lim, tot);
}

signed main(){
	freopen("escape.in","r",stdin);
	freopen("escape.ans","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)b[i] = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	dfs(1);
	for(int i = 1; i <= n; ++i)printf("%lld ",f[i]); printf("\n");
	return 0;
}

9

A. Delov的魔力

草,之前说要再做一个 SAM 的题,实在找不到拿来凑数的今天考了。。。。

就是求 lcs, 然后特别考虑一下没有相同字符的,跑全源最短路

code
#include<queue>
#include<iostream>
#include<cstring>
#include<map>
#include<climits>

using namespace std;
int read(){
	int x = 0; bool f = 0; 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 = 200005;
int n;
char c[maxn];
struct node{
    int fa, len;
    bool vis[3];
	map<int, int>ch;
}d[maxn << 4 | 1];
struct SAM{
    int las = 1, cnt = 1;
    void insert(int c, int str){
        if(d[las].ch[c]){
            int fa = las, x = d[las].ch[c];
            if(d[fa].len + 1 == d[x].len)las = x;
            else{
                int now = las = ++cnt;
                d[now] = d[x];
                d[now].len = d[fa].len + 1;
                for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = now;
                d[x].fa = now;
            }
        }else{    
            int fa = las;
            int now = las = ++cnt;
            d[now].len = d[fa].len + 1;
            for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
            if(!fa)d[now].fa = 1;
            else{
                int x = d[fa].ch[c];
                if(d[fa].len + 1 == d[x].len)d[now].fa = x;
                else{
                    int clone = ++cnt;
                    d[clone] = d[x];
                    d[clone].len = d[fa].len + 1;
                    d[x].fa = d[now].fa = clone;
                    for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
                }
            }
        }
        d[las].vis[str] = 1;
    }
    queue<int> q;
    int rd[maxn << 4 | 1];
    int work(){
        for(int i = 2; i <= cnt; ++i)++rd[d[i].fa];
        for(int i = 2; i <= cnt; ++i)if(rd[i] == 0)q.push(i);
        while(!q.empty()){
            int x = q.front(); q.pop();
            for(int i = 1; i <= n; ++i)d[d[x].fa].vis[i] += d[x].vis[i];
            --rd[d[x].fa];
            if(rd[d[x].fa] == 0)q.push(d[x].fa);
        }
        int ans = 0;
        for(int i = 2; i <= cnt; ++i){
            for(int j = 1; j <= 2; ++j){
                if(d[i].vis[j] == 0)break;
                if(j == 2)ans = max(ans, d[i].len);
            }
        }
		return ans;
    }
}S;
int a[maxn], s[maxn], t[maxn], la, lb;
vector<int>g[maxn];
int dis[maxn]; 
queue<int>q;
int sol(){
	for(int i = 1; i < n; ++i)g[a[i]].push_back(a[i + 1]), g[a[i + 1]].push_back(a[i]);
	memset(dis, 0x3f, sizeof(dis));
	for(int i = 1; i <= la; ++i)dis[s[i]] = 0, q.push(s[i]);
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int v : g[x]){
			if(dis[v] > dis[x] + 1){dis[v] = dis[x] + 1; q.push(v);}
		}
	}
	int ans = dis[0];
	for(int i = 1; i <= lb; ++i)ans = min(ans, dis[t[i]]);
	return la + lb + ans + ans - 2;
}
int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    cin >> n; for(int i = 1; i <= n; ++i)a[i] = read();
	S.las = 1; la = read(); for(int i = 1; i <= la; ++i)S.insert(s[i] = read(), 1);
	S.las = 1; lb = read(); for(int i = 1; i <= lb; ++i)S.insert(t[i] = read(), 2);
    int lcs = S.work();
	if(lcs)printf("%d\n",la - lcs + lb - lcs);
	else printf("%d\n",sol());
    return 0;
}

B. Mogohu-Rea Idol

结论题,op=oa+ob+oc

求三个凸包的闵可夫斯基和,判断点是否在凸包内即可

code
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<unordered_map>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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 = 600005;
const double eps = 1e-8;
int zfx(double x){return x > eps ? 1 : x < -eps ? -1 : 0;}
struct point{
	double x, y;
	void rd(){x = read(), y = read();}
	friend bool operator <(const point &x, const point &y){return zfx(x.x - y.x) ? x.x < y.x : x.y < y.y;}
	friend point operator + (const point &x, const point &y){return point{x.x + y.x, x.y + y.y};}
	friend point operator - (const point &x, const point &y){return point{x.x - y.x, x.y - y.y};}
	friend point operator * (const point &x, const double y){return point{x.x * y, x.y * y};}
}p0[maxn], p1[maxn], p2[maxn], p3[maxn], res[maxn], v1[maxn], v2[maxn];
double cha(point a, point b){return a.x * b.y - a.y * b.x;}
double shu(point a, point b){return a.x * b.x + a.y * b.y;}

bool online(point p, point a, point b){
	return !zfx(cha(p - a, p - b)) && zfx(shu(p - a, p - b)) < 0;
}
int n1, n2, n3, n;
int tb(point p[], int n, point ans[]){
	sort(p + 1, p + n + 1);
	int top = 0;
	for(int i = 1; i <= n; ++i){
		while(top > 1 && zfx(cha(ans[top] - ans[top - 1], p[i] - ans[top - 1]) <= 0))--top;
		ans[++top] = p[i];
	}
	int t = top;
	for(int i = n - 1; i >= 1; --i){
		while(top > t && zfx(cha(ans[top] - ans[top - 1], p[i] - ans[top - 1]) <= 0))--top;
		ans[++top] = p[i];
	}
	return --top;
}
int mkadd(point p_1[], int n1, point p_2[], int n2, point ans[]){
	for(int i = 1; i <= n1; ++i)v1[i] = p_1[i < n1 ? i + 1 : 1] - p_1[i];
	for(int i = 1; i <= n2; ++i)v2[i] = p_2[i < n2 ? i + 1 : 1] - p_2[i];
	int top = 0, p1 = 1, p2 = 1; ans[++top] = p_1[1] + p_2[1];
	while(p1 <= n1 && p2 <= n2)++top, ans[top] = ans[top - 1] + (zfx(cha(v1[p1], v2[p2])) > 0 ? v1[p1++] : v2[p2++]);
	while(p1 <= n1)++top, ans[top] = ans[top - 1] + v1[p1++];
	while(p2 <= n2)++top, ans[top] = ans[top - 1] + v2[p2++];
	return top;
}
int pip(point p[], int n, point now){
	if(zfx(cha(now - p[1], p[2] - p[1])) > 0 || zfx(cha(p[n] - p[1], now - p[1])) > 0)return 0;
	if(online(now, p[1], p[2]) || online(now, p[1], p[n]))return 1;
	int l = 2, r = n - 1;
	while(l < r){
		int mid = l + r + 1 >> 1;
		if(zfx(cha(now - p[1], p[mid] - p[1])) > 0)r = mid - 1;
		else l = mid;
	}
	return zfx(cha(now - p[r], p[r + 1] - p[r])) <= 0;
}
int main(){
	freopen("idol.in","r",stdin);
	freopen("idol.out","w",stdout);
	n1 = read(); for(int i = 1; i <= n1; ++i)p1[i].rd();
	n2 = read(); for(int i = 1; i <= n2; ++i)p2[i].rd();
	n3 = read(); for(int i = 1; i <= n3; ++i)p3[i].rd();
	n1 = tb(p1, n1, p0); for(int i = 1; i <= n1; ++i)p1[i] = p0[i];
	n2 = tb(p2, n2, p0); for(int i = 1; i <= n2; ++i)p2[i] = p0[i];
	n3 = tb(p3, n3, p0); for(int i = 1; i <= n3; ++i)p3[i] = p0[i];
	n = mkadd(p1, n1, p2, n2, p0); n = mkadd(p0, n, p3, n3, res);	
	n = tb(res, n, p0);
	int t = read();
	for(int i = 1; i <= t; ++i){
		point now; now.rd();
		if(pip(p0, n, now * 3))printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

C. 墙

现场学习 PIP, 判断每个点是否在某些多边形内即可

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

using namespace std;
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 = 2005;
int n, m;
struct wall{int a, b, c, d;}w[maxn];
map<pii, int>mp;
map<int, int>id;
struct dsu{
	int f[maxn];
	void init(){for(int i = 1; i <= n; ++i)f[i] = i;}
	int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
	void merge(int x, int y){f[fa(y)] = fa(x);}
}S;
vector<int>poly[maxn];
int cnt;
map<vector<bool>, int>res;
vector<bool>op[maxn];
bool check(int x, int y, int id){
	int res = 0;
	for(int i = 0; i < poly[id].size(); ++i){
		wall now = w[poly[id][i]];
		if(y >= min(now.b, now.d) && y < max(now.b, now.d)){
			res += (now.a + (double)(y - now.b) / (double)(now.d - now.b) * (now.c - now.a) - x > 1e-10);
		}
	}
	return res & 1;
}
int main(){
	freopen("wall.in","r",stdin);
	freopen("wall.out","w",stdout);
	n = read(), m = read();
	S.init();
	for(int i = 1; i <= n; ++i){
		w[i].a = read(); w[i].b = read();
		if(mp[pii(w[i].a, w[i].b)])S.merge(i, mp[pii(w[i].a, w[i].b)]);
		else mp[pii(w[i].a, w[i].b)] = i;
		w[i].c = read(); w[i].d = read();
		if(mp[pii(w[i].c, w[i].d)])S.merge(i, mp[pii(w[i].c, w[i].d)]);
		else mp[pii(w[i].c, w[i].d)] = i;
	}
	for(int i = 1; i <= n; ++i){
		if(!id[S.fa(i)])id[S.fa(i)] = ++cnt;
		poly[id[S.fa(i)]].push_back(i);
	}
	for(int i = 1; i <= m; ++i){
		int x = read(), y = read();
		for(int j = 1; j <= cnt; ++j)op[i].push_back(check(x, y, j));
	}
	int ans = 0;
	for(int i = 1; i <= m; ++i)++res[op[i]], ans = max(ans, res[op[i]]);
	printf("%d\n",ans);
	return 0;
}

专项测试

字符串1

A. 回文子串

线段树维护一个位置开始/结尾的在 k 范围内的子串

每次暴力重构两边,中间区间覆盖

发现字母也需要区间覆盖,则再开一棵树维护字母

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

using namespace std;

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

const int maxn = 50005;

int n, k, q;
char s[maxn];

char c[maxn << 1 | 1];
int m, p[maxn << 1 | 1], cnt[maxn << 1 | 1];

struct seg{
	struct node{
		char ch;
		int tag, laz, len, sum;
	}t[maxn << 2 | 1];
	void push_up(int x){t[x].sum = t[x << 1].sum + t[x << 1 | 1].sum;}
	void upd(int x, int val){t[x].sum = val * t[x].len; t[x].tag = val;}
	void upc(int x, char c){t[x].laz = t[x].ch = c;}
	void push_down(int x){
		if(t[x].tag != -1)upd(x << 1, t[x].tag), upd(x << 1 | 1, t[x].tag);
		if(t[x].laz != -1)upc(x << 1, t[x].laz), upc(x << 1 | 1, t[x].laz);
		t[x].laz = t[x].tag = -1;
	}
	void built(int x, int l, int r){
		t[x].tag = t[x].laz = -1; t[x].len = r - l + 1;
		if(l == r){ t[x].ch = s[l]; return;}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
	}
	void modify(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R){ upd(x, val); return;}
		push_down(x);
		int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	void update(int x, int l, int r, int L, int R, char c){
		if(L <= l && r <= R){ upc(x, c); return;}
		push_down(x);
		int mid = (l + r) >> 1;
		if(L <= mid)update(x << 1, l, mid, L, R, c);
		if(R > mid)update(x << 1 | 1, mid + 1, r, L, R, c);
	}
	int 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, 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; 
	}
	char get(int x, int l, int r, int pos){
		if(l == r)return t[x].ch;
		push_down(x);
		int mid = (l + r) >> 1;
		if(pos <= mid)return get(x << 1, l, mid, pos);
		else return get(x << 1 | 1, mid + 1, r, pos);
	}
}T;

void manacher(int l, int r){
	m = 0; c[0] = '?'; c[++m] = '#';
	for(int i = l; i <= r; ++i)c[++m] = T.get(1, 1, n, i), c[++m] = '#';
	c[++m] = '&';
	for(int i = 1; i <= m; ++i)cnt[i] = 0, p[i] = 0;
	int mr = 0, mid = 0;
	for(int i = 2; i < m; ++i){
		if(i < mr)p[i] = min(p[mid * 2 - i], mr - i);
		else p[i] = 1;
		while(c[i - p[i]] == c[i + p[i]])++p[i];
		if(i + p[i] > mr)mr = i + p[i], mid = i;
		++cnt[i - min(k, p[i]) + 1]; --cnt[i + 1];	
	}
	for(int i = 1; i <= m; ++i)cnt[i] += cnt[i - 1], p[i] = 0;
	// for(int i = 1; i <= m; ++i)printf("%d ", cnt[i]); printf("\n");
}
char ch[3];

void modify(int l, int r, char c){
	if(l <= r - k + 1)T.modify(1, 1, n, l, r - k + 1, k);
	T.update(1, 1, n, l, r, c);
	manacher(max(1, l - k + 1), min(n, l + k));
	for(int i = 2, pos = max(1, l - k + 1); i < m && pos <= l; i += 2, ++pos)
		T.modify(1, 1, n, pos, pos, min(k, cnt[i]));
	manacher(max(1, r - k + 2), min(n, r + k));
	for(int i = 2, pos = max(1, r - k + 2); pos <= r && i < m; i += 2, ++pos)
		T.modify(1, 1, n, pos, pos, min(k, cnt[i]));
}

ll query(int l, int r){
	ll ans = 0;
	if(l <= r - k)ans += T.query(1, 1, n, l, r - k);
	manacher(max(l, r - k + 1), r);
	for(int i = 2; i < m; i += 2)ans += cnt[i];
	return ans;
}

int main(){
	scanf("%s", s + 1);
	scanf("%d%d",&k,&q);
	n = strlen(s + 1);
	T.built(1, 1, n);
	manacher(1, n);
	for(int i = 1; i <= n; ++i)T.modify(1, 1, n, i, i, cnt[i << 1]);
	for(int i = 1, op, l, r; i <= q; ++i){
		scanf("%d%d%d",&op, &l, &r);
		if(op & 1){
			scanf("%s",ch + 1);
			modify(l, r, ch[1]);
		}else printf("%lld\n",query(l, r));
	}
	return 0;
}

B. recollection

lcp 是两个点 trie 树上的 lca 深度

lcs 是两个点 fail 树上的 lca 长度

令深度最深,则只需考虑 dfs 序相邻的两个点

那么在一个树上维护另外一个树的点的 dfs 序集,每次启发式合并即可

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

using namespace std;

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

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

const int maxn = 800005;

struct node{
	int len, fa;
	map<int, int>ch;
}d[maxn];
int up[maxn], pos[maxn];
int n;
queue<int>q;
vector<int>t[maxn], g[maxn];
int son[maxn], dep[maxn], fa[maxn], si[maxn], top[maxn], id[maxn], dfn[maxn], tim;
int tf[maxn], td[maxn];

void dfs1(int x){
	si[x] = 1;
	for(int v : g[x]){
		dep[v] = dep[x] + 1; fa[v] = x;
		dfs1(v);
		si[x] += si[v];
		son[x] = si[son[x]] > si[v] ? son[x] : v;
	}
}
void dfs2(int x, int tp){
	top[x] = tp; dfn[x] = ++tim; id[tim] = x;
	if(son[x])dfs2(son[x], tp);
	for(int v : g[x])if(v != son[x]){
		dfs2(v, v);
	}
}
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}

struct SAM{
	int cnt = 1;
	int ins(int las, int c){
		int fa = las, now = las = ++cnt;
		d[now].len = d[fa].len + 1;
		for(; fa && !d[fa].ch[c]; fa = d[fa].fa)d[fa].ch[c] = now;
		if(!fa)d[now].fa = 1;
		else{
			int x = d[fa].ch[c];
			if(d[x].len == d[fa].len + 1)d[now].fa = x;
			else{
				int clone = ++cnt; d[clone] = d[x];
				d[clone].len = d[fa].len + 1;
				d[now].fa = d[x].fa = clone;
				for(; fa && d[fa].ch[c] == x; fa = d[fa].fa)d[fa].ch[c] = clone;
			}
		}
		return now;
	}
	void built(){for(int i = 2; i <= cnt; ++i)g[d[i].fa].push_back(i);}
}S;
int ans;
set<int>s[maxn];
int merge(set<int>&x, set<int>&y){
	if(x.size() < y.size())swap(x, y);
	int res = 0;
	for(int v : y){
		auto it = x.lower_bound(v);
		if(it != x.end())res = max(res, d[lca(id[v], id[*it])].len);
		if(it != x.begin())res = max(res, d[lca(id[v], id[*--it])].len);
	}
	for(int v : y)x.insert(v); y.clear();
	return res;
}
void solve(int x){
	s[x].insert(dfn[pos[x]]);
	for(int v : t[x]){
		solve(v);
		ans = max(ans, merge(s[x], s[v]) + td[x]);
	}
}

int main(){
	n = read();
	for(int i = 2; i <= n; ++i){
		tf[i] = read(), up[i] = read();
		t[tf[i]].push_back(i);
	}
	q.push(1); pos[1] = 1;
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int v : t[x]){
			pos[v] = S.ins(pos[x], up[v]);
			q.push(v); td[v] = td[x] + 1;
		}
	}
	S.built();dfs1(1); dfs2(1, 1);
	solve(1);
	printf("%d\n",ans);
	return 0;
}

C. 回忆树

分三部分处理,跨越 lca 的一小段暴力 kmp

另外两段考虑写成差分的形式,然后离线下来放到 AC 自动机上做

对询问串建立 AC 自动机,然后 dfs 原树跑匹配,匹配到了询问串就加一

用树状数组维护子树和即可

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

using namespace std;

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

const int maxn = 2e5 + 55;
int n, m, tot, head[maxn];
struct edge{
	int to, net; char val;
}e[maxn << 1 | 1];
char s[maxn], up[maxn];
int len;
void add(int u, int v, char c){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
	e[tot].val = c;
}
struct Q{int id, pos, opt;};
vector<Q>que[maxn];
int ans[maxn];
int dfn[maxn], si[maxn], tim;
struct AC{
	vector<int>g[maxn];
	int ch[maxn][26], fail[maxn], cnt = 1;
	int ins(int id){
		int now = 1;
		for(int i = 1; i <= len; ++i){
			if(!ch[now][s[i] - 'a'])ch[now][s[i] - 'a'] = ++cnt;
			now = ch[now][s[i] - 'a'];
		}
		return now;
	}
	queue<int>q;
	void built(){
		for(int i = 0; i < 26; ++i)if(ch[1][i])q.push(ch[1][i]), fail[ch[1][i]] = 1; else ch[1][i] = 1;
		while(!q.empty()){
			int x = q.front(); q.pop();
			for(int i = 0; i < 26; ++i)
				if(ch[x][i])fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
				else ch[x][i] = ch[fail[x]][i];
		}
		for(int i = 2; i <= cnt; ++i)g[fail[i]].push_back(i);
	}
	void dfs(int x){
		dfn[x] = ++tim;
		si[x] = 1;
		for(int v : g[x])dfs(v), si[x] += si[v];
	}
}A;

int fa[maxn][21], dep[maxn];
void dfs(int x){
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x][0])continue;
		fa[v][0] = x; up[v] = e[i].val; dep[v] = dep[x] + 1;
		dfs(v); 
	}
}
int lca(int u, int v){
	if(dep[u] < dep[v])swap(u, v);
	for(int i = 20; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))u = fa[u][i];
	if(u == v)return u;
	for(int i = 20; i >= 0; --i)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}
int kfa(int x, int k){
	if(k <= 0)return x;
	for(int i = 20; i >= 0; --i)if(k & (1 << i))x = fa[x][i];
	return x;
}
char c[maxn], st[maxn];
int nxt[maxn], lenc;
void get_c(int u, int v){
	int la = 0, lb = 0;
	while(u != v){
		if(dep[u] > dep[v]){
			c[++la] = up[u];
			u = fa[u][0];
		}else{
			st[++lb] = up[v];
			v = fa[v][0];
		}
	}
	for(int i = lb; i >= 1; --i)c[++la] = st[i];
	lenc = la;
}
void kmp(){
	for(int i = 2, j = 0; i <= len; ++i){
		while(j &&s[i] !=s[j + 1])j = nxt[j];
		if(s[i] ==s[j + 1]) ++j;
		nxt[i] = j;
	}
}
int match(){
	int res = 0;
	for(int now = 0, i = 1; i <= lenc; ++i){
		while(now && s[now + 1] != c[i])now = nxt[now];
		if(s[now + 1] == c[i]) ++now;
		if(now == len)++res, now = nxt[now];
	}
	return res;
}
struct BIT{
	int t[maxn];
	int lowbit(int x){return x & -x;}
	void modify(int x, int val){while(x <= tim){t[x] += val; x += lowbit(x);}}
	int query(int x){int res = 0; while(x){res += t[x]; x -= lowbit(x);}return res;}
	int calc(int x){return query(dfn[x] + si[x] - 1) - query(dfn[x] - 1);}
}T;
void sol(int x, int y){
	T.modify(dfn[y], 1);
	for(Q q : que[x])ans[q.id] += q.opt * T.calc(q.pos);
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v != fa[x][0])sol(v, A.ch[y][up[v] - 'a']);
	}
	T.modify(dfn[y], -1);
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1, u, v; i < n; ++i){
		scanf("%d%d%s",&u,&v,s + 1);
		add(u, v, s[1]); add(v, u, s[1]);
	}
	dfs(1);
	for(int j = 1; j <= 20; ++j)
		for(int i = 1; i <= n; ++i)fa[i][j] = fa[fa[i][j - 1]][j - 1];
	for(int i = 1, u, v; i <= m; ++i){
		scanf("%d%d%s",&u,&v,s + 1); len = strlen(s + 1);
		int posb = A.ins(i); reverse(s + 1, s + len + 1);
		int posa = A.ins(i); reverse(s + 1, s + len + 1);
		int lc = lca(u, v), fu = kfa(u, dep[u] - dep[lc] - len + 1), fv = kfa(v, dep[v] - dep[lc] - len + 1);
		if(dep[u] - dep[lc] >= len){
			que[fu].push_back({i, posa, -1});
			que[u].push_back({i, posa, 1});
		}
		if(dep[v] - dep[lc] >= len){
			que[fv].push_back({i, posb, -1});
			que[v].push_back({i, posb, 1});
		}
		get_c(fu, fv); kmp(); ans[i] = match();
	}
	A.built(); A.dfs(1);
	sol(1, 1);
	for(int i = 1; i <= m; ++i)printf("%d\n",ans[i]);
	return 0;
}

数学1

A. young

nbDP

fn,m 表示 n 个点,取值范围 [0,2m1] 最小生成树边权和

考虑当前位为 1/0 可以划分两个集合 s,t 那么他们一定内部连边,然后用一条边连起来

考虑计算这条边的贡献

如果该位都相同

+=2fn,m1

否则枚举一个集合大小

i=1n1(ni)(2(ni)(m1)fi,m1+2i(m1)fni,m1+gs,t,m1+2(n+1)(m1))

其中 2(n+1)(m1)=2m12ni2i2m1 方案数乘贡献

gs,t,m 表示集合大小为 s,t 的两点集之间连一条边的价值和,点权在 [0,2m1] 之间

之间求不好做,考虑求最小边权大于等于某数的方案数

gs,t,m=i=12m1ps,t,m,i

ps,t,m,k 表示集合 s,t 点权 [0,2m1], 最小边权大于等于 k 的方案数

考虑把 s,t 按照该位为 1/0 划分成 s0,t0,s1,t1然后考虑这位的贡献

k=0 或者一个点集空了,那么剩下的点权就没有限制了,边界为 2m(s+t)

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int mod = 258280327;
const int maxn = 259;
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 c[55][55], p2[maxn * maxn];
int rf[55][maxn];
int rg[55][55][maxn];
int rp[55][55][maxn][maxn];
int p(int s, int t, int m, int k){
	if(s > t)swap(s, t);
	if(s == 0 || k <= 0)return p2[(s + t) * m];
	if(k >= (1 << m))return 0;
	if(rp[s][t][m][k])return rp[s][t][m][k];
	int ans = 0;
	if((k & (1 << (m - 1))))
		ans = 2ll * p(s, t, m - 1, k ^ (1 << (m - 1))) % mod;
	else{
		for(int s0 = 0; s0 <= s; ++s0){
			for(int t0 = 0; t0 <= t; ++t0){
				int s1 = s - s0, t1 = t - t0;
				if((s1 == 0 && t0 == 0) || (s0 == 0 && t1 == 0)) ans = (ans + p(s, t, m - 1, k - (1 << (m - 1)))) % mod;
				else ans = (ans + 1ll * c[s][s0] * c[t][t0] % mod * p(s0, t0, m - 1, k) % mod * p(s1, t1, m - 1, k)) % mod;
			}
		}	
	}
	return rp[s][t][m][k] = ans;
}

int g(int s, int t, int m){
	if(m == 0)return 0;
	if(s > t)swap(s, t);
	if(rg[s][t][m])return rg[s][t][m];
	int ans = 0;
	for(int k = 1; k < (1 << m); ++k)ans = (ans + p(s, t, m, k)) % mod;
	return rg[s][t][m] = ans;
}
int f(int n, int m){
	if(m == 0 || n == 1)return 0;
	if(rf[n][m])return rf[n][m];
	int ans = 2 * f(n, m - 1) % mod;
	for(int i = 1; i < n; ++i){
		int res = (1ll * p2[(n - i) * (m - 1)] * f(i, m - 1) % mod + 1ll * p2[i * (m - 1)] * f(n - i, m - 1) % mod) % mod;
		res = (0ll + res + g(i, n - i, m - 1) + p2[(n + 1) * (m - 1)]) % mod;
		ans = (ans + 1ll * c[n][i] * res) % mod;
	}
	return rf[n][m] = ans;
}
int n, m;
int main(){
	freopen("young.in","r",stdin);
	freopen("young.out","w",stdout);
	n = read(), m = read();
	p2[0] = 1;
	for(int i = 1; i <= 1005; ++i)p2[i] = (p2[i - 1] + p2[i - 1]) % mod;
	for(int i = 0; i <= n; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= i; ++j)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	}
	printf("%lld\n",1ll * f(n, m) * qpow(qpow(2, n * m), mod - 2) % mod);
	return 0;
}

B. Simple

想错了

首先,有循环节的一定不满足,没有循环节的找到最小表示法方案唯一

于是枚举循环节长度得到

fn=d|n10ndμ(d)n

答案为

i=1ni2fi=i=1nid|i10idμ(d)=d=1ndμ(d)i=1ndi10i

后面那个等比乘等差就套路搞搞就行。。。。

前面那个杜教筛 (μ×id)id=ϵ

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int mod = 258280327, mx = 1000000, maxn = mx + 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;
}
int mu[maxn], prime[maxn], cnt, f[maxn];
bool flag[maxn];
void init(){
	mu[1] = 1;
	for(int i = 2; i <= mx; ++i){
		if(!flag[i])prime[++cnt] = i, mu[i] = -1;
		for(int j = 1; j <= cnt && i * prime[j] <= mx; ++j){
			flag[i * prime[j]] = true;
			if(i % prime[j] == 0)break;
			mu[i * prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i <= mx; ++i)f[i] = (f[i - 1] + i * mu[i]) % mod;
}
ll n;
unordered_map<int, int>mp;
ll calc(ll l, ll r){return 1ll * (l + r) % mod * (r - l + 1) % mod * ((mod + 1) >> 1) % mod;}
int F(ll n){
	if(n <= mx)return f[n];
	if(mp.count(n))return mp[n];
	int ans = 1;
	for(ll l = 2, r; l <= n; l = r + 1){
		r = n / (n / l);
		ans = (ans - 1ll * calc(l, r) * F(n / l)) % mod;
	}
	return mp[n] = (ans + mod) % mod;
}
int G(ll n){
	int tp = qpow(10, (n + 1) % (mod - 1));
	int res = 1ll * (tp - 10) * qpow(9, mod - 2) % mod;
	res = (1ll * n * tp - res) % mod;
	res = 1ll * res * qpow(9, mod - 2) % mod;
	return res;
}
int main(){
	// freopen("simple.in","r",stdin);
	// freopen("simple.out","w",stdout);	
	cin >> n; init();
	int ans = 0;
	for(ll l = 1, r; l <= n; l = r + 1){
		r = n / (n / l);
		ans = (ans + 1ll * (F(r) - F(l - 1)) * G(n / l)) % mod;
	}
	ans = (ans % mod + mod) % mod;
	printf("%d\n",ans);
	return 0;
}

C. mate

之前考过式子相同的,好像还有个 nb 式子

我写的 exlucas+lucas ,复杂度不太对劲,会 T 飞,好在数据水

之前做过那个写法挺好,就是把数写成 P 的质因子的幂次和非 P 的质因子的部分相乘的形式

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 1000005;
ll qpow(ll x, ll y, ll mod){
	ll ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}

ll exgcd(ll a, ll b, ll &x, ll &y){
	if(b == 0){x = 1; y = 0; return a;}
	ll gcd = exgcd(b, a % b, y, x);
	y -= a / b * x; return gcd;
}
ll inv(ll a, ll b){
	ll x, y; exgcd(a, b, x, y);
	return (x % b + b) % b;
}
typedef pair<int, int> pii;
map<pii, int>mp;
ll get_f(ll n, ll p, ll pk){
	if(n == 0)return 1;
	if(mp[pii(n, p)])return mp[pii(n, p)];
	ll res = 1, tmp = n % pk;
	for(ll i = 2; i <= tmp; ++i)if(i % p)res = 1ll * res * i % pk;
	ll num = res;
	if(n / pk)for(ll i = tmp + 1; i < pk; ++i)if(i % p)res = 1ll * res * i % pk;
	return mp[pii(n, p)] = 1ll * get_f(n / p, p, pk) * qpow(res, n / pk, pk) % pk * num % pk;
}
ll get_c(ll n, ll m, ll p, ll pk){
	ll res = 0, d = n - m;
	for(ll i = p; i <= n; i *= p)res += n / i;
	for(ll i = p; i <= m; i *= p)res -= m / i;
	for(ll i = p; i <= d; i *= p)res -= d / i;
	return 1ll * qpow(p, res, pk) * get_f(n, p, pk) % pk * inv(1ll * get_f(m, p, pk) * get_f(d, p, pk) % pk, pk) % pk;
}

int mod, p[105], pk[105], cnt, fac[105][maxn], ifac[105][maxn];
int c(int n, int m, int p, int i){return 1ll * fac[i][n] * ifac[i][n - m] % p * ifac[i][m] % p;}
int lucas(int n, int m, int p, int i){
	if(m > n)return 0;
	if(m == n || n == 0 || n == 1)return 1;
	if(n < p && m < p)return c(n, m, p, i);
	return 1ll * lucas(n % p, m % p, p, i) * lucas(n / p, m / p, p, i) % p;
}
void init(){
	int x = mod;
	for(ll i = 2; i * i <= x; ++i){
		if(x % i)continue;
		int tmp = 1; 
		while(x % i == 0)x /= i, tmp *= i;
		++cnt; p[cnt] = i; pk[cnt] = tmp;
	}
	if(x != 1){++cnt; p[cnt] = pk[cnt] = x;}
	for(int k = 1; k <= cnt; ++k)if(p[k] == pk[k]){
		fac[k][0] = ifac[k][0] = 1;
		int up = min(p[k], 1000001);
		for(int i = 1; i < up; ++i)fac[k][i] = 1ll * fac[k][i - 1] * i % p[k];
		ifac[k][up - 1] = qpow(fac[k][up - 1], p[k] - 2, p[k]) % p[k];
		for(int i = up - 2; i >= 1; --i)ifac[k][i] = 1ll * ifac[k][i + 1] * (i + 1) % p[k]; 	
	}
}
ll exlucas(ll n, ll m){
	if(n == m)return 1;
	ll ans = 0;
	for(int i = 1; i <= cnt; ++i){
		ll c = mod / pk[i], getc = 0;
		if(p[i] == pk[i])getc = lucas(n, m, p[i], i);
		else getc = get_c(n, m, p[i], pk[i]);
		ans = (ans + 1ll * getc * c % mod * inv(c, pk[i]) % mod) % mod;
	}
	return ans;
}
int n, x, y;
int main(){
	freopen("mate.in","r",stdin);
	freopen("mate.out","w",stdout);
	cin >> n >> mod >> x >> y; init();
	if(x < 0) x = -x; if(y < 0)y = -y;
	int res = n - x - y;
	if(res & 1)printf("0\n");
	else{
		int up = res / 2;
		ll ans = 0;
		for(int i = 0; i <= up; ++i){
			ans = (ans + 1ll * exlucas(n, x + i + i) * exlucas(x + i + i, i) % mod * exlucas(y + (up - i) * 2, up - i) % mod) % mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

数学2

A. 猜拳游戏

如果得到胜负概率,那么高斯消元即可得到答案

但是有个平局非常恶心,简单思考发现只需要重新分配概率即可

即令p,q 表示考虑平局时两人胜利的概率

把胜率改成 pp+q 即可

然后不会了

学个套路

pp+q=11+qp 求最大值

qp 最小值

形如分数规划,考虑二分答案

qp<=mid

q<=pmid

0<=pmidq

于是就可以 DP 了,不要写记搜,常数太大

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 2005, base = 1002;
int n, m1, m2;
double p[maxn][3], winp;
double f[maxn][maxn + maxn], a[maxn + maxn][maxn + maxn];
double mid;
bool check(){
	for(int i = base + 1; i <= base + n; ++i)f[n + 1][i] = mid;
	for(int i = base - 1; i >= base - n; --i)f[n + 1][i] = -1;
	f[n + 1][base] = 0;
	for(int round = n; round >= 1; --round){
		for(int wind = base - round + 1; wind <= base + round - 1; ++wind){
			double p1 = p[round][0] * f[round + 1][wind] + p[round][1] * f[round + 1][wind - 1] + p[round][2] * f[round + 1][wind + 1];
			double p2 = p[round][0] * f[round + 1][wind + 1] + p[round][1] * f[round + 1][wind] + p[round][2] * f[round + 1][wind - 1];
			double p3 = p[round][0] * f[round + 1][wind - 1] + p[round][1] * f[round + 1][wind + 1] + p[round][2] * f[round + 1][wind];
			f[round][wind] = max({p1, p2, p3}); 
		}
	}
	return f[1][base] > 1e-8;
}
void gauss(int n){
	for(int i = 0; i <= n; ++i){
		int now = i;
		for(int j = i + 1; j <= n; ++j)if(abs(a[j][i]) > abs(a[now][i]))now = j;
		for(int j = i; j <= n + 1; ++j)swap(a[now][j], a[i][j]);
		for(int j = n + 1; j >= i; --j)a[i][j] /= a[i][i];
		for(int j = 0; j <= n; ++j)if(i != j)
			for(int k = n + 1; k >= i; --k)
				a[j][k] -= a[j][i] * a[i][k];
	}
}
int main(){
	n = read(), m1 = read(), m2 = read();
	while(n || m1 || m2){
		for(int i = 1; i <= n; ++i){
			p[i][0] = read() / 100.0;
			p[i][1] = read() / 100.0;
			p[i][2] = read() / 100.0;
		}
		double l = 0, r = 1e6;
		while(r - l > 1e-6){
			mid = (l + r) / 2;
			if(check() > 0)r = mid;
			else l = mid;
		}
		winp = (l + r) / 2;
		winp = 1 / (1 + winp);
		for(int i = 0; i <= m1 + m2 + 1; ++i)
			for(int j = 0; j <= m1 + m2 + 1; ++j)
				a[i][j] = (i == j);
		for(int i = 1; i < m1 + m2; ++i)a[i][i + 1] = -winp, a[i][i - 1] = winp - 1;
		a[m1 + m2][m1 + m2 + 1] = 1;
		gauss(m1 + m2);
		
		printf("%.5lf\n",a[m2][m1 + m2 + 1]);
		n = read(), m1 = read(), m2 = read();
	}
	return 0;
}

B. B君的回忆

结论题吧大概,就是猜测打表发现有循环节

然后 bsgs 求解

优化在于一个结论,设 len(x) 表示模 x 意义下循环节长度

len(x)=lcm(len(pici))

len(pici)=len(pi)pici1

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
int mod;
struct matrix{
	ll a[2][2];
	matrix(){a[0][0] = a[1][0] = a[0][1] = a[1][1] = 0;}
	void I(){a[0][0] = a[1][1] = 1; a[1][0] = a[0][1] = 0;}
	static matrix II(){ matrix x; x.I(); return x;}
	friend matrix operator * (const matrix &x, const matrix &y){
		matrix ans;
		ans.a[0][0] = ( x.a[0][0] * y.a[0][0] +  x.a[0][1] * y.a[1][0]) % mod;
		ans.a[0][1] = ( x.a[0][0] * y.a[0][1] +  x.a[0][1] * y.a[1][1]) % mod;
		ans.a[1][0] = ( x.a[1][0] * y.a[0][0] +  x.a[1][1] * y.a[1][0]) % mod;
		ans.a[1][1] = ( x.a[1][0] * y.a[0][1] +  x.a[1][1] * y.a[1][1]) % mod;
		return ans;
	}
	void qpow(int k){
		matrix ans; ans.I(); 
		for(; k; k >>= 1, (*this) = (*this) * (*this))if(k & 1)ans = ans * (*this);
		(*this) = ans;
	}
	friend bool operator ==(const matrix& x, const matrix& y){
		return memcmp(&x, &y, sizeof(x)) == 0;
	}
}trans, f1;
struct hash_mat{
	size_t operator ()(const matrix& x)const{
		static const int B = 97;
		size_t val = 0;
		for(int i = 0; i < 2; ++i){
			for(int j = 0; j < 2; ++j){
				val = val * B + x.a[i][j];
			}
		}
		return val;
	}
};
int a, b, n, k, p;
int g(int n, int p){
	if(n == 0)return a;
	if(n == 1)return b;
	mod = p; matrix now = f1;
	matrix tr = trans; tr.qpow(n - 1); 
	now = now * tr;
	return  (now.a[0][0] % p + p) % p;
}
unordered_map<matrix, int, hash_mat>mp;
unordered_map<int, int>rb;

int bsgs(int p){
	if(rb.count(p))return rb[p];
	int sq = 2 * ceil(sqrt(p));
	mp.clear(); mod = p;
	matrix x, tr; x.I(); mp[x] = 0;
	tr = trans; tr.a[1][0] = p - 1;
	for(int i = 1; i < sq; ++i){
		x = x * tr; mp[x] = i;
	}
	x = x * tr;
	matrix y = x;
	for(int i = 1; i <= sq; ++i){
		if(mp.count(y))return rb[p] = i * sq - mp[y];
		y = y * x;
	}
	abort();
}
unordered_map<int, int>rp;
int prime[1005], cprime[1005], cnt;
int getmod(int p){
	if(rp.count(p))return rp[p];
	int x = p; cnt = 0;
	for(int i = 2; i * i <= x; ++i)if(x % i == 0){
		prime[++cnt] = i; cprime[cnt] = 0;
		while(x % i == 0)x /= i, ++cprime[cnt];
	}
	if(x > 1)prime[++cnt] = x,cprime[cnt] = 1;
	int ans = 1;
	for(int i = 1; i <= cnt; ++i){
		int len = bsgs(prime[i]);
		for(int j = 1; j < cprime[i]; ++j)len *= prime[i];
		ans = ans / __gcd(ans, len) * len;
	}
	return rp[p] = ans;
}
int f(int n, int k, int p){
	if(k == 0)return n;
	return g(f(n, k - 1, getmod(p)), p);
}

int main(){
	trans.a[0][0] = 3; trans.a[0][1] = 1; trans.a[1][0] = -1;
	int t = read();
	for(int i = 1; i <= t; ++i){
		a = read(), b = read(), n = read(), k = read(), p = read();
		f1.a[0][0] = b; f1.a[0][1] = a;
		printf("%d\n",f(n, k, p));
	}
	return 0;
}

C. 小 H 爱染色

神仙组合意义题

i=0n1F(i)H(ni)

H(n) 表示 n 个球染色,并且第一个黑色的方案数

F 展开

j=0mi=0n1H(ni)ij

然后开始胡扯

考虑后面的实际含义,对于 H 就是上面的解释, iji 个球,每次染色一个

我们枚举了一个 i, 而分开两部分考虑两种染色

那么我们可以认为前 i 个为第二种染色,剩下的为第一种染色

那么 i 其实可以转化掉

如果枚举前后两部分染黑 t,k

那么第 t+1 个位置就是 i, 但是在哪里并不重要了

考虑算出此时的方案

选出这t+k 个位置 (nt+k)

第一种染色,考虑先选出 m 个染色, km 个没染的下一次必定染, m(km) 个则需要在之前染过的 m 个选择

(km)(mm+mk)

第二种染色,二项式反演转化成至多

i=0t(ti)(1)i(ti)j

整个式子变成

j=0mfjt=0mk=m2m(nt+k)(km)(m2mk)i=0t(ti)(1)i(ti)j

fj 放到后面,发现恰好成了 F(ti)

t=0mk=m2m(nt+k)(km)(m2mk)i=0t(ti)(1)iF(ti)

先卷后面 i=0t(ti)(1)iF(ti)

然后卷上 k=m2m(km)(m2mk)

最后乘上 (nt+k)

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 5000005;
const int mod = 998244353;
int fac[maxn], ifac[maxn];
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int deg, wn[maxn], rev[maxn];
void init(int len){
	deg = 1; while(deg < len)deg <<= 1;
	wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
	for(int i = 1; i < deg; ++i){
		wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
		rev[i] = rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= (deg >> 1);
	}
}
struct poly{
	int f[maxn];
	int &operator [](const int &i){return f[i];}
	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){
				int x, y;
				for(int j = i; j < i + hl; ++j){
					x = f[j], y = 1ll * wn[deg / l * (j - i)] * f[j + hl] % mod; 
					f[j] = (x + y) % mod; f[j + hl] = (x - y + mod) % mod;
				}
			}
	}
	void operator *= (poly &g){
		ntt(); g.ntt();
		for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * g[i] % mod;
		intt();
	}
	void intt(){
		ntt(); 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;
	}
}f, g;
void pre(int mx){
	fac[0] = ifac[0] = 1;  for(int i = 1; i <= mx; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	ifac[mx] = qpow(fac[mx], mod - 2); for(int i = mx - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
}
int c(int n, int m){return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int n, m;

int main(){
	n = read(), m = read(); pre(3 * m);
	init(m + m + 5);
	for(int i = 0; i <= m; ++i)f[i] = 1ll * read() * ifac[i] % mod;
	for(int i = 0; i <= m; ++i)g[i] = (i & 1) ? mod - ifac[i] : ifac[i];
	f *= g; for(int i = 0; i <= m; ++i)f[i] = 1ll * f[i] * fac[i] % mod;
	for(int i = m + 1; i < deg; ++i)f[i] = 0;
	for(int i = 0; i < deg; ++i)g[i] = 0;
	for(int i = m; i <= m + m; ++i)g[i - m] = 1ll * c(i, m) * c(m, m + m - i) % mod;
	f *= g;
	int binom = ifac[m];
	for(int i = 0; i < m; ++i)binom = 1ll * binom * (n - i) % mod;
	int ans = 0;
	for(int i = m; i <= m * 3; i++){
		ans = (ans + 1ll * binom * f[i - m]) % mod;
		binom = 1ll * binom * (n - i) % mod * fac[i] % mod * ifac[i + 1] % mod;
	}
	printf("%d\n",ans);
	return 0;
}

数学3

A. 解方程

没有限制直接插板法

有限制 >=x

在总数上减去 x1 ,先给他 x1 个,则转换为插板法

对于 <=x 进行容斥,枚举哪些不合法

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 1000005;
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;
}
ll qpow(ll x, ll y, ll mod){
	ll ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}

ll exgcd(ll a, ll b, ll &x, ll &y){
	if(b == 0){x = 1; y = 0; return a;}
	ll gcd = exgcd(b, a % b, y, x);
	y -= a / b * x; return gcd;
}
ll inv(ll a, ll b){
	ll x, y; exgcd(a, b, x, y);
	return (x % b + b) % b;
}
typedef pair<int, int> pii;
map<pii, int>mp;
ll get_f(ll n, ll p, ll pk){
	if(n == 0)return 1;
	if(mp[pii(n, p)])return mp[pii(n, p)];
	ll res = 1, tmp = n % pk;
	for(ll i = 2; i <= tmp; ++i)if(i % p)res = 1ll * res * i % pk;
	ll num = res;
	if(n / pk)for(ll i = tmp + 1; i < pk; ++i)if(i % p)res = 1ll * res * i % pk;
	return mp[pii(n, p)] = 1ll * get_f(n / p, p, pk) * qpow(res, n / pk, pk) % pk * num % pk;
}
ll get_c(ll n, ll m, ll p, ll pk){
	ll res = 0, d = n - m;
	for(ll i = p; i <= n; i *= p)res += n / i;
	for(ll i = p; i <= m; i *= p)res -= m / i;
	for(ll i = p; i <= d; i *= p)res -= d / i;
	return 1ll * qpow(p, res, pk) * get_f(n, p, pk) % pk * inv(1ll * get_f(m, p, pk) * get_f(d, p, pk) % pk, pk) % pk;
}

int mod, p[105], pk[105], cnt, fac[105][maxn], ifac[105][maxn];
int c(int n, int m, int p, int i){return 1ll * fac[i][n] * ifac[i][n - m] % p * ifac[i][m] % p;}
int lucas(int n, int m, int p, int i){
	if(m > n)return 0;
	if(m == n || n == 0 || n == 1)return 1;
	if(n < p && m < p)return c(n, m, p, i);
	return 1ll * lucas(n % p, m % p, p, i) * lucas(n / p, m / p, p, i) % p;
}
void init(){
	int x = mod;
	for(ll i = 2; i * i <= x; ++i){
		if(x % i)continue;
		int tmp = 1; 
		while(x % i == 0)x /= i, tmp *= i;
		++cnt; p[cnt] = i; pk[cnt] = tmp;
	}
	if(x != 1){++cnt; p[cnt] = pk[cnt] = x;}
	for(int k = 1; k <= cnt; ++k)if(p[k] == pk[k]){
		fac[k][0] = ifac[k][0] = 1;
		int up = min(p[k], 1000001);
		for(int i = 1; i < up; ++i)fac[k][i] = 1ll * fac[k][i - 1] * i % p[k];
		ifac[k][up - 1] = qpow(fac[k][up - 1], p[k] - 2, p[k]) % p[k];
		for(int i = up - 2; i >= 1; --i)ifac[k][i] = 1ll * ifac[k][i + 1] * (i + 1) % p[k]; 	
	}
}
ll exlucas(ll n, ll m){
	if(n == m)return 1;
	ll ans = 0;
	for(int i = 1; i <= cnt; ++i){
		ll c = mod / pk[i], getc = 0;
		if(p[i] == pk[i])getc = lucas(n, m, p[i], i);
		else getc = get_c(n, m, p[i], pk[i]);
		ans = (ans + 1ll * getc * c % mod * inv(c, pk[i]) % mod) % mod;
	}
	return ans;
}
int n, x, y, m, lim[maxn];
signed main(){
	int t = read(); mod = read(); 
	init();
	for(int r = 1; r <= t; ++r){
		n = read(), x = read(), y = read(), m = read();
		for(int i = 1; i <= x; ++i)lim[i] = read();
		for(int i = 1; i <= y; ++i)m -= read() - 1;
		int ans = 0;
		for(int i = 0; i < (1 << x); ++i){
			int mm = m, opt = 1;
			for(int j = 1; j <= x; ++j)if((1 << (j - 1)) & i)opt *= -1, mm -= lim[j];
			if(mm < n)continue;
			ans = (ans + opt * exlucas(mm - 1, n - 1));
		}
		ans = (ans % mod + mod) % mod;
		printf("%d\n",ans);
	}
	return 0;
}

B. 宇宙序列

暴力的话直接做 fwt2np

发现 fwt 之后,每个位置最终答案只与现在该位置的数有关,而且值域较小

于是考虑对每个数求出答案,最后统一 ifwt 回去

使用倍增,答案 fx,k=j=02k1x2j

fx,k=fx,k1+fx22k1

code
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<unordered_map>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

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

int qpow(int x, int y, int mod){
	int ans = 1; 
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
const int maxn = 600005;
const int mod = 10007;
void fwt(int f[], int deg, int opt){
	for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
		for(int i = 0; i < deg; i += l)
			for(int j = i; j < i + hl; ++j){
				int x = f[j], y = f[j + hl];
				f[j] = 1ll * opt * (x + y) % mod;
				f[j + hl] = 1ll * opt * (x - y + mod) % mod;
			}
}
int n, p, k, f[maxn], g[maxn];
int dp[33][maxn];
int main(){
	n = read(), p = read(), k = read();
	for(int i = 0; i < (1 << n); ++i)f[i] = read();
	fwt(f, 1 << n, 1);
	for(int i = 0; i < mod; ++i)dp[0][i] = i;
	for(int i = 1; i < 30; ++i)
		for(int j = 0; j < mod; ++j)
			dp[i][j] = (dp[i - 1][j] + dp[i - 1][qpow(j, qpow(2, 1 << (i - 1), mod - 1), mod)]) % mod;
	for(int i = 0; i < mod; ++i){
		int x = i;
		for(int j = 29; j >= 0; --j)if((p + 1) & (1 << j)){
			g[i] = (g[i] + dp[j][x]) % mod;
			x = qpow(x, qpow(2, 1 << j, mod - 1), mod);
		}
	}
	for(int i = 0; i < (1 << n); ++i)f[i] = g[f[i]];
    fwt(f, 1 << n, (mod + 1) >> 1);
	printf("%d\n",f[k]);
	return 0;
}

C. exp

https://www.cnblogs.com/skyh/p/12134383.html

code
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<unordered_map>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 405;
double c[maxn][maxn], pw[maxn][maxn];
char s[maxn];
int n, cnt[maxn][maxn];
bool rf[maxn][maxn], rp[maxn][maxn];
double p[maxn][maxn];
double P(int l, int r);
double G(int l, int r, int k){
	int a = cnt[l][k], b = cnt[k + 1][r];
	if(s[r] == 'X' || s[k] == 'X')return 0;
	return pw[k - l + 1][a] * pw[r - k][b - 1] / pw[r - l + 1][a + b - 1] * c[a + b - 2][a - 1] * P(l, k) * P(k + 1, r);
}

double P(int l, int r){
	if(rp[l][r])return p[l][r]; p[l][r] = 0; 
	if(s[r] == 'X')return 0;
	if(cnt[l][r] == 1)return 1;
	rp[l][r] = 1;
	for(int i = l; i < r; ++i)p[l][r] += G(l, r, i);
	// printf("%d %d %lf\n",l, r, p[l][r]);
	
	return p[l][r];
}
double f[maxn][maxn];
double F(int l, int r){
	if(rf[l][r])return f[l][r];
	f[l][r] = 0; rf[l][r] = 1;
	if(s[r] == 'X' || P(l, r) < 1e-12 || cnt[l][r] == 1)return 0;
	for(int i = l; i < r; ++i)f[l][r] += G(l, r, i) * (F(l, i) + F(i + 1, r) + (i - l) / 2.0);
	f[l][r] /= P(l, r);
	// printf("%d %d %lf\n",l, r, f[l][r]);
	return f[l][r];
}
void solve(){
	scanf("%s",s + 1);
	n = strlen(s + 1);
	for(int i = 1; i <= n; ++i)s[i + n] = s[i];
	for(int i = 1; i <= n + n; ++i)cnt[i][i] = s[i] == '.';
	for(int i = 1; i <= n + n; ++i)
		for(int j = i + 1; j <= n + n; ++j)
			cnt[i][j] = cnt[i][j - 1] + (s[j] == '.');
	if(cnt[1][n] == 0){printf("0\n"); return;}
	memset(rf, 0, sizeof(rf));
	memset(rp, 0, sizeof(rp));
	double ans = (n - 1) / 2.0;
	for(int i = 1; i <= n; ++i)ans += F(i, i + n - 1) * P(i, i + n - 1);
	printf("%.8lf\n",ans);
}

int main(){
	for(int i = 0; i <= 400; ++i){
		c[i][0] = pw[i][0] = 1;
		for(int j = 1; j <= i; ++j)
			c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
		for(int j = 1; j <= 400; ++j)
			pw[i][j] = pw[i][j - 1] * i;
	}
	int t; scanf("%d",&t);
	for(int r = 1; r <= t; ++r)solve();
	return 0;
}

posted @   Chen_jr  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示