CSP-S模拟16

A. 猜道路

直接弗洛伊德即可,不知道为啥脑抽,想了半年建最小生成树

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

using namespace std;
typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 505;
int n, tot, cnt;
int mp[maxn][maxn];
bool del[maxn][maxn];

int main(){
	n = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			mp[i][j] = read();
	bool fl = 1;
	for(int k = 1; k <= n && fl; ++k)
		for(int i = 1; i <= n && fl; ++i)
			for(int j = 1; j <= n; ++j)		
				if(i != k && i != j && j != k){
					if(mp[i][k] + mp[k][j] < mp[i][j]){
						fl = 0; break;
					}
					if(mp[i][k] + mp[k][j] == mp[i][j])del[i][j] = del[j][i] = 1;
				}
	if(fl){
		ll ans = 0;
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j < i; ++j)
				if(!del[i][j])ans += mp[i][j];
		printf("%lld\n",ans);
	}else printf("-1\n");
	return 0;
}

B. 简单环

考场脑抽,想了半年建树

确实建树,然后发现一个环只要与另外一个环有交就都不合法,于是随机权值异或

不过好像 \(Tarjan\) 一下就行?

不管了

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

using namespace std;
typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
mt19937_64 rd((ull)(new char) * (ull)(new char));
ll srd(){
	uniform_int_distribution<ll>d(1, 9223372036854775807);
	return d(rd);
}
const int maxn = 1000005;
int f[maxn];
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
int head[maxn], tot;
struct edge{int to, net, id;}e[maxn << 1 | 1];
void add(int u, int v, int w){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
	e[tot].id = w;
}
int ru[maxn], rv[maxn], rc[maxn], rid[maxn], cnt;
ll rval[maxn], val[maxn];
int n, m;
int son[maxn], size[maxn], top[maxn], dep[maxn], id[maxn];
void dfs1(int x){
	size[x] = 1;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == f[x])continue;
		dep[v] = dep[x] + 1;
		f[v] = x;
		id[v] = e[i].id;
		dfs1(v);
		size[x] += size[v];
		son[x] = size[son[x]] > size[v] ? son[x] : v; 
	}
}
void dfs2(int x, int tp){
	top[x] = tp;
	if(son[x])dfs2(son[x], tp);
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == f[x] || v == son[x])continue;
		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 = f[top[u]];
	}
	if(dep[u] < dep[v])return u;
	return v;
}
unordered_map<ll, int>mp;
void dfs3(int x){
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == f[x])continue;
		dfs3(v);
		val[x] ^= val[v];
	}
	if(x != 1)++mp[val[x]];
}
vector<int>v;
int main(){
	n = read(), m = read();
	for(int i = 1; i <= maxn - 3; ++i)f[i] = i;
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		if(fa(u) == fa(v)){
			++cnt; ru[cnt] = u; rv[cnt] = v; rval[cnt] = srd(); rid[cnt] = i;
		}else{add(u, v, i); add(v, u, i); f[fa(u)] = fa(v);}
	}
	for(int i = 1; i <= n; ++i)f[i] = 0;
	for(int i = 1; i <= n; ++i)if(dep[i] == 0)dfs1(i), dfs2(i, i);
	for(int i = 1; i <= cnt; ++i){
		int lc = lca(ru[i], rv[i]);
		rc[i] = dep[ru[i]] + dep[rv[i]] - dep[lc] - dep[lc];
		val[ru[i]] ^= rval[i]; val[rv[i]] ^= rval[i];
	}
	for(int i = 1; i <= n; ++i)if(dep[i] == 0)dfs3(i);
	int ans = 0;
	for(int i = 1; i <= cnt; ++i){
		cerr << ru[i] << " " << rv[i] << endl;
		if(mp[rval[i]] != rc[i])continue;
		ans += rc[i] + 1;
		int lc = lca(ru[i], rv[i]);
		int u = ru[i];
		while(dep[u] && u != lc){v.push_back(id[u]); u = f[u];}
		u = rv[i];
		while(dep[u] && u != lc){v.push_back(id[u]); u = f[u];}
		v.push_back(rid[i]);
	}
	if(ans)sort(v.begin(), v.end());
	printf("%d\n",ans);
	for(int x : v)printf("%d ",x);
	return 0;
}

C. 汉明距离

开始看到时限 \(15000ms\) 加上脑抽,算出来直接暴力能过》

然而不行

正解 \(FFT\) 科技,比较离谱

\(H(x)\) 表示从 \(x\) 开始的答案

\(H(x ) = \sum_{i =x}^{x + m - 1}(a_i - b_i)^2 = \sum_{i = x}^{x + m - 1}a_i^2+b_i^2-2a_ib_i\)

两个平方项前缀和预处理,后面那个发现是差卷积的形式,取的是卷积后第 \(x\) 项系数

差卷积可以直接 \(nfft\) \(g\) 但是不除以 \(n\) ,然后与 \(fft\)\(f\) 卷即可

比较神奇,多项式。。。。。如果。。。。。。。。。

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

using namespace std;
typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 3000005;
const int mod = 998244353;
ll qpow(ll x, ll y){
	ll ans = 1;
	for(; y; y >>= 1, x = x * x % mod)if(y & 1)ans = ans * x % mod;
	return ans;
}
int rev[maxn];
void getrev(int len){
	for(int i = 0; i < len; ++i){
		rev[i] = rev[i >> 1] >> 1;
		if(i & 1)rev[i] |= len >> 1;
	}
}
inline void ntt(int a[], int len){
	for(int i = 1; i < len; ++i)if(i < rev[i]) swap(a[i], a[rev[i]]);
	for(int l = 2; l <= len; l <<= 1){
		ll wn = qpow(3, (mod - 1) / l), p = l >> 1;
		for(int j = 0; j < len; j += l){
			ll w = 1;
			for(int k = j; k < j + p; ++k){
				ll x = a[k], y = w * a[k + p] % mod;
				a[k] = (x + y) % mod; a[k + p] = (x - y + mod) % mod;
				w = w * wn % mod; 
			}
		}
	}
}
void intt(int a[], int len, bool op){
	ntt(a, len); reverse(a + 1, a + len);
	if(op)return;
	ll inv = qpow(len, mod - 2);
	for(int i = 0; i < len; ++i)a[i] = a[i] * inv % mod;
}
int f[maxn], g[maxn], h[maxn];

char a[maxn], b[maxn];
int la, lb, sa[maxn], sb[maxn];
int main(){
	scanf("%s%s",a, b);
	la = strlen(a); lb = strlen(b);
	for(int i = 0; i < la; ++i)f[i] = a[i] - '0';
	for(int i = 0; i < lb; ++i)g[i] = b[i] - '0';
	sa[0] = a[0] == '1';for(int i = 1; i < la; ++i)sa[i] = sa[i - 1] + a[i] - '0'; 
	sb[0] = b[0] == '1';for(int i = 1; i < lb; ++i)sb[i] = sb[i - 1] + b[i] - '0'; 
	int mx = 1; while(mx < la + lb  - 1)mx <<= 1;
	getrev(mx);
	ntt(f, mx); 
	intt(g, mx, 1);
	for(int i = 0; i < mx; ++i)h[i] = 1ll * f[i] * g[i] % mod;
	intt(h, mx, 0);
	int ans = lb;
	for(int i = 0; i + lb <= la; ++i){
		if(i) ans = min(ans, ((sa[i + lb - 1] - sa[i - 1] + sb[lb - 1] - 2 * h[i]) % mod + mod) % mod);
		else ans = min(ans, ((sa[i + lb - 1] + sb[lb - 1] - 2 * h[i]) % mod + mod) % mod);
	}
	printf("%d\n",(ans % mod + mod) % mod);
	return 0;
}

D. 勇者的后缀

我是 *****

考场写出 \(sa\) 但是只会暴力
。。。。。。。。。。。。。。。。。

然后建主席树,在对应区间查询 \(rk_{i}\) 的前驱后继,取 \(maxlcp\)\(len\), 然后在前面二分得到 \(min(rk)\) 使得 \(lcp(minrk,x) == len\) 然后在主席树对应区间查询后继

主席树无法直接查前驱后继,先查排名然后查排名\(+/- 1\)即可

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

using namespace std;
typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 200055;
int root[maxn];
struct pst{
	struct node{
		int l, r, size;
	}t[maxn * 30];
	int cnt;
	void push_up(int x){t[x].size = (t[t[x].l].size + t[t[x].r].size);}
	void insert(int &x, int y, int l, int r, int pos){
		if(!x) x = ++cnt;
		if(l == r){
			assert(x);
			++t[x].size;
			return;
		}
		int mid = (l + r) >> 1;
		t[x] = t[y];
		if(pos <= mid)t[x].l = 0, insert(t[x].l, t[y].l, l, mid, pos);
		else t[x].r = 0, insert(t[x].r, t[y].r, mid + 1, r, pos);
		push_up(x);
	}
	int get_rk(int x, int y, int l, int r, int val){
		if(t[x].size - t[y].size == 0)return 0;
		if(l == r){
			if(l <= val)return t[x].size;
			return 0;
		}
		int mid = (l + r) >> 1;
		if(val <= mid)return get_rk(t[x].l, t[y].l, l, mid, val);
		else return t[t[x].l].size - t[t[y].l].size + get_rk(t[x].r, t[y].r, mid + 1, r, val);
	}
	int que_rk(int x, int y, int l, int r, int rk){
		if(rk < 1 || t[x].size - t[y].size < rk)return -1;
		if(l == r)return l;
		int mid = (l + r) >> 1;
		if(rk <= t[t[x].l].size - t[t[y].l].size)return que_rk(t[x].l, t[y].l, l, mid, rk);
		else return que_rk(t[x].r, t[y].r, mid + 1, r, rk - t[t[x].l].size + t[t[y].l].size);
	}
}t;
int sa[maxn], rk[maxn], ht[maxn], cnt[maxn + maxn], ork[maxn + maxn], px[maxn + maxn], id[maxn];
bool cmp(int x, int y, int w){return ork[x] == ork[y] && ork[x + w] == ork[y + w];}
char c[maxn];
struct SA{
	int n, m = 300;
	void built(){
		m = max(n, m);
		for(int i = 1; i <= n; ++i)++cnt[rk[i] = c[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 i = 1; i <= m; ++i)cnt[i] = 0;
		int p = 0;
		for(int w = 1; 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 <= 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 <= m; ++i)cnt[i] = 0;
			p = 0;
			for(int i = 1; i <= n; ++i)ork[i] = rk[i];
			for(int i = 1; i <= n; ++i){
				if(cmp(sa[i], sa[i - 1], w))rk[sa[i]] = p;	
				else rk[sa[i]] = ++p;
			}
			if(p == n)break;
			m = p;
		}
	}
	void get_ht(){
		int k = 0;
		for(int i = 1; i <= n; ++i){
			if(k) --k;
			while(c[i + k] == c[sa[rk[i] - 1] + k]) ++k;
			ht[rk[i]] = k;
		}
	}
	int st[maxn][19];
	int lg[maxn];
	void get_st(){
		for(int i = 2; i <= n; ++i)lg[i] = lg[i >> 1] + 1;
		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]);
	}
	int get_lcp_pos(int x, int y){
		if(x == y)return n - x + 1;
		x = rk[x], y = rk[y];
		if(x > y)swap(x, y);
		++x;
		int k = lg[y - x + 1];
		return min(st[x][k], st[y - (1 << k) + 1][k]);
	}
	int get_lcp_rk(int x, int y){
		if(x > y)swap(x, y);
		++x; int k = lg[y - x + 1];
		return min(st[x][k], st[y - (1 << k) + 1][k]);
	}
	int bound(int l, int r, int len, int qrk){
		int ans = r; --r;
		while(l <= r){
			int mid = (l + r) >> 1;
			if(get_lcp_rk(mid, qrk) == len)ans = mid, r = mid - 1;
			else l = mid + 1;
		}
		return ans;
	}
	void solve(){
		int q = read();
		for(int ask = 1; ask <= q; ++ask){
			int x = read(), l = read(), r = read();
			int rkx = t.get_rk(root[r], root[l - 1], 1, n, rk[x]);
			int prk = t.que_rk(root[r], root[l - 1], 1, n, rkx);
			int nrk = t.que_rk(root[r], root[l - 1], 1, n, rkx + 1);
			int len = 0, pos = 0, lenl = 0, lenr = 0;
			if(prk != -1)lenl = prk == rk[x] ? n - x + 1 : get_lcp_rk(prk, rk[x]);
			if(nrk != -1)lenr = get_lcp_rk(rk[x], nrk);
			len = max(lenl, lenr);
			if(prk != -1 && len == lenl){
				int rkm = bound(1, prk, len, rk[x]);
				int rkrkm = t.get_rk(root[r], root[l - 1], 1, n, rkm - 1) + 1;
				int posrk = t.que_rk(root[r], root[l - 1], 1, n, rkrkm);
				pos = sa[posrk];
			}else pos = sa[nrk];
			printf("%d %d\n", len, pos);
		}
	}
	void init(){
		scanf("%s",c + 1);
		n = strlen(c + 1);
		built(); get_ht(); get_st();
		for(int i = 1; i <= n; ++i)t.insert(root[i], root[i - 1], 1, n, rk[i]);
		solve();
	}
}s;
int main(){
	s.init();
	return 0;
}
posted @ 2022-10-03 19:25  Chen_jr  阅读(23)  评论(0编辑  收藏  举报