2022NOIP A层联测29

从今天开始学习写一句话题解

A

把合法的序列搜出来建 \(AC\)自动机,大于 \(max(p,q,r)\)的合并,然后就是求长度为 \(n\) ,匹配过关键点的串数量,容易解决

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

using namespace std;

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

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

const int maxn = 55;
const int mod = 998244353;
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int n, m, p, q, r, mx;
int f[maxn][100005][2];
int a[maxn];
struct AC{
	int son[100005][7], root = 1, cnt = 1, fail[100005];
	bool flag[100005];
	void insert(int len){
		int now = root;
		for(int i = 1; i <= len; ++i){
			if(!son[now][a[i]])son[now][a[i]] = ++cnt; 
			now = son[now][a[i]];
		}
		flag[now] = true;
	}
	queue<int>q;
	void built(){
		for(int i = 0; i <= mx; ++i)
			if(son[root][i])q.push(son[root][i]), fail[son[root][i]] = root;
			else son[root][i] = root;
		while(!q.empty()){
			int x = q.front(); q.pop();
			for(int i = 0; i <= mx; ++i)
				if(son[x][i])q.push(son[x][i]), fail[son[x][i]] = son[fail[x]][i];
				else son[x][i] = son[fail[x]][i];
		}
	}
	int trans(){
		f[0][1][0] = 1;
		for(int len = 0; len < n; ++len)
			for(int i = 1; i <= cnt; ++i)if(f[len][i][0] || f[len][i][1]){
				for(int j = 1; j <= mx; ++j){
					int v = son[i][j], fl = flag[v];
					add(f[len + 1][v][fl], f[len][i][0]);
					add(f[len + 1][v][1], f[len][i][1]);
				}
				if(m > mx){
					add(f[len + 1][1][0], 1ll * f[len][i][0] * (m - mx) % mod);
					add(f[len + 1][1][1], 1ll * f[len][i][1] * (m - mx) % mod);
				}
			}
		int ans = 0;
		for(int i = 1; i <= cnt; ++i)add(ans, f[n][i][1]);
		return ans;
	}
}A;

void dfs(int pos, int sp, int sq, int sr){
	if(pos > n + 1)return;
	if(sp < p)
		for(int i = 1; i <= min(p - sp, m); ++i)a[pos] = i, dfs(pos + 1, sp + i, sq, sr);
	else{
		if(sq < q)
			for(int i = 1; i <= min(q - sq, m); ++i)a[pos] = i, dfs(pos + 1, sp, sq + i, sr);
		else{
			if(sr < r) 
				for(int i = 1; i <= min(r - sr, m); ++i) a[pos] = i, dfs(pos + 1, sp, sq, sr + i);
			else A.insert(pos - 1);
		}
	}
}
int sol(){
	dfs(1, 0, 0, 0);
	mx = max({p, q, r}); mx = min(mx, m);
	A.built();
	return A.trans();
}
int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n = read(), m = read(), p = read(), q = read(), r = read();
	printf("%d\n",sol());
	return 0;
}

B

扩展欧拉定理

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = 1000006;
ll mod, phi;
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;
}
char a[maxn], b[maxn];
ll la, lb, n, k;
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	scanf("%s%s",a + 1, b + 1);
	scanf("%lld",&mod);
	la = strlen(a + 1); lb = strlen(b + 1);
	int m = mod; phi = mod;
	for(int i = 2; i * i <= m; ++i)if((m % i) == 0){
		phi = phi / i * (i - 1); 
		while(m % i == 0)m /= i;
	}
	if(m != 1){phi = phi / m * (m - 1);}
	for(int i = 1; i <= la; ++i)n = (n * 10 + (a[i] ^ 48)) % phi;
	bool flag = false; 
	if(la <= 10){
		ll now = 0;
		for(int i = 1; i <= la; ++i)now = (now * 10 + (a[i] ^ 48));
		if(now < phi)flag = true;
	}
	for(int i = 1; i <= lb; ++i)k = (k * 10 + (b[i] ^ 48)) % mod;
	n = (n + phi - 1) % phi;
	ll ans = 1ll * (k - 1) * qpow(k, flag ? n : n + phi) % mod;
	printf("%lld\n",ans);
	return 0;
}

C

容易证明最优解一定是 \(u - m - v - m - w\)

(考虑反证)

然后枚举一下 \(m\) 即可

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 = 300005;

int dis[3][maxn];
ll val[maxn];
queue<int>q;
vector<int>g[maxn];
int n, m;
void bfs(int s, int id){
	for(int i = 1; i <= n; ++i)dis[id][i] = maxn;
	dis[id][s] = 0; q.push(s);
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int v : g[x])if(dis[id][v] > dis[id][x] + 1){
			dis[id][v] = dis[id][x] + 1;
			q.push(v);
		}
	}
}
int x, y, z;
int main(){
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	n = read(), m = read(), x = read(), y = read(), z = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read(); val[i] = read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	sort(val + 1, val + m + 1);
	for(int i = 1; i <= m; ++i)val[i] += val[i - 1];
	bfs(x, 0); bfs(y, 1); bfs(z, 2);
	ll ans = 4e18;
	for(int i = 1; i <= n; ++i){
		if(dis[0][i] + dis[1][i] + dis[2][i] <= m){
			ll now = val[dis[1][i]] + val[dis[0][i] + dis[1][i] + dis[2][i]];
			ans = min(ans, now);
		}
	}
	printf("%lld\n",ans);
	return 0;
}

D

容易发现,跟联通块个数有关,如果度数为偶数,欧拉回路即可

如果存在奇数,那么偶数显然不用管,只有把奇数消完就行

然后一个联通块提笔次数为 \(\sum [deg \& 1] / 2 - 1\) 因为每次可以消去两个奇度数

分横竖线考虑

先合并线段,然后进行排序,依次考虑每条横/竖线,按顺序处理交点

\(map\) 记一下点的编号映射

其实就没了

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

using namespace std;

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

int read(){
	int x = 0;bool f = 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 = 2005;
struct DSU{
	int f[maxn * maxn * 10];
	int fa(int x){return x == f[x] ? x : f[x] = fa(f[x]);}
	void merge(int x, int y){f[fa(y)] = fa(x);}
}S;
struct Line{
	int x1, x2, y;
	friend bool operator < (const Line &x, const Line &y){return x.y < y.y;}
}h[maxn], z[maxn];
int n, m, ch, cz;
bool check(Line H, Line Z){return H.x1 <= Z.y && H.x2 >= Z.y && Z.x1 <= H.y && Z.x2 >= H.y;}
bool cover(Line L, Line R){return L.y == R.y && ((L.x1 >= R.x1 && L.x1 <= R.x2) || (L.x2 >= R.x1 && L.x2 <= R.x2));}
bool del[maxn];
void uniqu(Line a[], int &len){
	int nlen = 0;
	for(int i = 1; i <= len; ++i)del[i] = false;
	for(int i = 1; i <= len; ++i)
		for(int j = 1; j < i; ++j)if(!del[j] && (cover(a[i], a[j]) || cover(a[j], a[i]))){
			a[i].x1 = min(a[i].x1, a[j].x1);
			a[i].x2 = max(a[i].x2, a[j].x2);
			del[j] = true;
		}
	for(int i = 1; i <= len; ++i)if(!del[i])a[++nlen] = a[i];
	len = nlen;
}
int tot, deg[maxn * maxn * 10], ans;
map<pii, int> id;
void link(int u, int v){
//	printf("link : %d %d\n",u, v);
	++deg[u]; ++deg[v]; S.merge(u, v);
}
void New(pii now){
//	printf("new : %d %d\n",now.first, now.second);
	id[now] = ++tot; S.f[tot] = tot;
}
void LINK(){
	for(int i = 1; i <= ch; ++i){
		pii now = pii(h[i].x1, h[i].y);
		if(!id[now])New(now); int las = id[now];
		for(int j = 1; j <= cz; ++j)if(check(h[i], z[j])){
			pii now = pii(z[j].y, h[i].y); 
			if(!id[now])New(now);
			if(las != id[now])link(las, id[now]);
			las = id[now];
		}
		now = pii(h[i].x2, h[i].y);
		if(!id[now])New(now);
		if(id[now] != las)link(las, id[now]);
	}
	for(int i = 1; i <= cz; ++i){
		pii now = pii(z[i].y, z[i].x1);
		if(!id[now])New(now); int las = id[now];
		for(int j = 1; j <= ch; ++j)if(check(h[j], z[i])){
			pii now = pii(z[i].y, h[j].y); 
			if(!id[now])New(now);
			if(las != id[now])link(las, id[now]);
			las = id[now];
		}
		now = pii(z[i].y, z[i].x2);
		if(!id[now])New(now);
		if(id[now] != las)link(las, id[now]);
	}
}
int val[maxn * maxn * 10];
void CUT(){
	ans = -1;
	for(int i = 1; i <= tot; ++i)ans += S.fa(i) == i;
	if(!(m & 1))return;
	for(int i = 1; i <= tot; ++i)if(deg[i] & 1)++val[S.fa(i)];
	for(int i = 1; i <= tot; ++i)if(S.fa(i) == i)ans = (ans + max(val[i] / 2 - 1, 0));
//	for(int i = 1; i <= tot; ++i)if(S.fa(i) == i)printf("%d ",val[i]); printf("\n");
}
int main(){
	freopen("d.in","r",stdin);
	freopen("d.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= n; ++i){
		int a = read(), b = read(), c = read(), d = read();
		if(a == c)z[++cz] = {min(b, d), max(b, d), a};
		else h[++ch] = {min(a, c), max(a, c), b};
	}
	//for(int i = 1; i <= ch; ++i)printf("%d %d %d %d\n", h[i].x1, h[i].y, h[i].x2, h[i].y);
	//for(int i = 1; i <= cz; ++i)printf("%d %d %d %d\n", z[i].y, z[i].x1, z[i].y, z[i].x2);
	uniqu(z, cz); uniqu(h, ch); sort(z + 1, z + cz + 1); sort(h + 1, h + ch + 1);
	//for(int i = 1; i <= ch; ++i)printf("%d %d %d %d\n", h[i].x1, h[i].y, h[i].x2, h[i].y);
	//for(int i = 1; i <= cz; ++i)printf("%d %d %d %d\n", z[i].y, z[i].x1, z[i].y, z[i].x2);
	LINK(); CUT();
	printf("%d\n",ans);
	return 0;
}

好像写了不止一句,,,,

下次主要

posted @ 2022-11-17 19:58  Chen_jr  阅读(10)  评论(0编辑  收藏  举报