Live2D

2021-05-27 集训题解

T1

Description

给出一个大小为 \(n\) 的序列 \(a_{1,2,...,n}\),求出一个长度为 \(n\) 的序列 \(b_{1,2,..,n}\) 使得任意一个 \(a_i\) 都可以通过 \(b_j+b_k\) 求到。

\(n\le 30\)

Solution

首先如果存在 \(a_i\) 为偶数的话,那么一定有解。

如果我们把每一个 \(a_i\) 的拆分看作一条边,那么,\(b\) 序列就是一个 \(n\) 个点 \(n\) 条边的图。所以一定存在环。

假设我们环的大小为 \(k\),环上的 \(b\)\(b_1,b_2,...,b_k\),那么我们可以得到 \(b_1+b_2=a_1,b_2+b_3=a_2,...,b_k+b_1=a_k\),我们就可以得到:

\[\sum_{i=1}^{k} a_i=2\sum_{i=1}^{k} b_i \]

因为 \(a_i\) 为偶数的情况已经判断了,所以 \(k\) 一定是偶数。

所以

\[a_1+a_3+...+a_{k-1}=\sum_{i=1}^{k} b_i=a_2+a_4+...+a_k \]

所以合法的情况就是存在两个大小相等的集合使得 \(\sum a\) 相同。这个可以折半搜索,复杂度 \(\Theta(3^{n/2})\)

Code(写的太丑了,过不了)

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

#define Int register int
#define W 1000000000009
#define int long long
#define MAXN 35

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,len,a[MAXN];
 
unordered_map <int,int> mp;

#define pii pair<int,int>

bool flg;
pii fucans;

void dfs (int now,int lim,int Sta,int pw,int totA1,int totA2,int SumA1,int SumA2,bool tag){
	if (flg) return ;
	if (now > lim){
		if (tag == 0) mp[SumA1 - SumA2 + (totA1 - totA2) * W] = Sta;
		else{
			int S = mp[SumA2 - SumA1 + (totA2 - totA1) * W];
			if (S) fucans = make_pair (S,Sta),flg = 1;
		}
		return ;
	}
	dfs (now + 1,lim,Sta,pw << 2,totA1,totA2,SumA1,SumA2,tag);
	dfs (now + 1,lim,Sta + pw,pw << 2,totA1 + 1,totA2,SumA1 + a[tag * len + now],SumA2,tag);
	dfs (now + 1,lim,Sta + pw * 2,pw << 2,totA1,totA2 + 1,SumA1,SumA2 + a[tag * len + now],tag);
}

signed main(){
	read (n),len = n >> 1;vector <int> S;
	for (Int i = 1;i <= n;++ i) read (a[i]);
	for (Int i = 1;i <= n;++ i) if (a[i] % 2 == 0){
		S.push_back (a[i] / 2);
		for (Int j = 1;j <= n;++ j) if (i ^ j) S.push_back (a[j] - a[i] / 2);
		goto there;
	}
	dfs (1,len,0,1,0,0,0,0,0),
	dfs (1,n - len,0,1,0,0,0,0,1);
	if (flg){
		vector <int> A1,A2;int S1 = fucans.first,S2 = fucans.second;
		for (Int i = 1;i <= len;++ i){
			int t = (S1 >> ((i - 1) * 2) & 3);
			if (t == 1) A1.push_back (a[i]);
			else if (t == 2) A2.push_back (a[i]);  
			else S.push_back (a[i]); 
		}
		for (Int i = 1;i <= n - len;++ i){
			int t = (S2 >> ((i - 1) * 2) & 3);
			if (t == 1) A1.push_back (a[len + i]);
			else if (t == 2) A2.push_back (a[len + i]);  
			else S.push_back (a[len + i]); 
		}
		S.push_back (0); 
		for (Int i = 0,lst = 0;i < A1.size();++ i){
			S.push_back (A1[i] - lst),lst = A1[i] - lst;
			if (i != A2.size() - 1) S.push_back (A2[i] - lst),lst = A2[i] - lst;  
		}
		goto there;
	}
	puts ("No");return 0;
	there:
	for (Int i = 1;i <= n;++ i){
		bool tag = 0;
		for (Int y1 = 0;y1 < n;++ y1)
			for (Int y2 = 0;y2 < n;++ y2)
				if (S[y1] + S[y2] == a[i]) tag = 1;
		if (!tag){
			puts ("No");
			return 0;
		}
	}
	puts ("Yes");
	for (Int i = 0;i < n;++ i) write (S[i]),putchar (' ');putchar ('\n');
	for (Int i = 1;i <= n;++ i){
		for (Int y1 = 0;y1 < n;++ y1)
			for (Int y2 = 0;y2 < n;++ y2)
				if (S[y1] + S[y2] == a[i]){
					cout << y1 + 1 << " " << y2 + 1 << endl;
					goto here;
				}
		here:;
	}	
	return 0;
}

T2

Description

有一个 \(n\) 个点,\(m\) 条边的图,满足所有点双大小 \(\le s\),有一些点已经钦定了颜色,还有一些可以从 \([1,k]\) 中任选。问最小颜色联通数。

\(n\le 10^5,k\le 20,s\le 6\)

Solution

可以先直接建出广义圆方树,注意原点一定连方点,这样好讨论。

考虑设 \(f_{u,c}\) 表示圆方树上以 \(u\) 为根的子树 \(u\)\(c\) 的最小操作次数,如果 \(u\) 为方点,表示 \(u\) 的父亲染 \(c\)

那么如果 \(u\) 是圆点,转移式就是:

\[f_{u,c}=1+\sum_{v\in son_u} (f_{v,c}-1) \]

如果 \(u\) 是方点,我们就需要多讨论一下。

我们设 \(g_{S,c}\) 表示点双中的一个连通块 \(S\) 都染颜色 \(c\) 的最小花费。

那么我们可以得到:

如果 \(S\) 联通 \(g_{S,c}=1+\sum_{u\in S} (f_{u,c}-1)\)

否则 \(g_{S,c}=\infty\)

判联通可以递推预处理。

\(G(S)=\max(g_{S,c})\)\(h(S)\) 表示解决掉 \(S\) 的最小花费。

可以得到 \(h(S)=G(T)+h(S-T)\),而我们的 \(f_{u,c}=\min(g_{S,c}+h(H-S))\),其中 \(H\) 表示全集。

复杂度是 \(\Theta(n(3^s+2^s\times s))\) 的。

Code

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

#define Int register int
#define MAXN 1000005
#define inf 1900007

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,m,K,s;
void chkmin (int &a,int b){a = min (a,b);}

struct edge{
	int v,nxt;
}e[MAXN << 1];
int toop = 1,head[MAXN];

void add_edge (int u,int v){
	e[++ toop] = edge {v,head[u]},head[u] = toop;
	e[++ toop] = edge {u,head[v]},head[v] = toop;
}

int tp,ind,cnt,c[MAXN],dfn[MAXN],low[MAXN],stk[MAXN];
vector <int> G[MAXN];
void link (int u,int v){
	G[u].push_back (v),G[v].push_back (u); 
}

void Tarjan (int u,int pre){
	dfn[u] = low[u] = ++ ind,stk[++ tp] = u;
	for (Int i = head[u];i;i = e[i].nxt) if ((i ^ 1) != pre){
		int v = e[i].v;
		if (!dfn[v]){
			Tarjan (v,i),low[u] = min (low[u],low[v]);
			if (low[v] >= dfn[u]){
				link (u,++ cnt);
				do{
					link (cnt,stk[tp]);
				}while (stk[tp --] != v);
			}
		}
		else low[u] = min (low[u],dfn[v]);
	}
}

bool uni[1 << 7];
int sz,f[MAXN][21],h[1 << 7],g[MAXN][1 << 7],id[MAXN],ch[MAXN],nxtS[MAXN];
void dfs (int u,int fa){
	if (u <= n){
		for (Int i = 1;i <= K;++ i) if (!c[u] || c[u] == i) f[u][i] = 1;else f[u][i] = inf;
		for (Int v : G[u]) if (v ^ fa){
			dfs (v,u);
			for (Int i = 1;i <= K;++ i) f[u][i] += f[v][i] - 1;
		}
	}
	else{
		for (Int v : G[u]) if (v ^ fa) dfs (v,u);
		sz = 0;
		for (Int v : G[u]) id[v] = sz,ch[sz ++] = v;
		for (Int v : G[u]){
			for (Int i = head[v];i;i = e[i].nxt){
				int r = e[i].v;
				if (id[r] >= 0) nxtS[r] |= (1 << id[v]),nxtS[v] |= (1 << id[r]);
			}
		}
		int up = (1 << sz) - 1;uni[0] = 1;
		for (Int S = 1;S <= up;++ S){
			uni[S] = 0;
			for (Int i = 0;i < sz;++ i) if (S >> i & 1){
				int lst = S ^ (1 << i);
				if (uni[lst] && (!lst || (nxtS[ch[i]] & lst))){
					uni[S] = 1;
					break;
				}
			}
			if (!uni[S]){
				for (Int i = 0;i <= K;++ i) g[S][i] = inf;
				continue;
			}
			for (Int i = 1;i <= K;++ i) g[S][i] = 1;
			for (Int i = 0;i < sz;++ i) if ((S >> i & 1) && ch[i] != fa)
				for (Int z = 1;z <= K;++ z) g[S][z] += f[ch[i]][z] - 1;
			g[S][0] = inf;
			for (Int i = 1;i <= K;++ i) chkmin (g[S][0],g[S][i]);
		}
		h[0] = 0;
		for (Int S = 1;S <= up;++ S){
			h[S] = inf;
			for (Int T = S;T;T = (T - 1) & S) chkmin (h[S],g[T][0] + h[S ^ T]);
		}
		for (Int c = 1;c <= K;++ c){
			f[u][c] = inf;
			for (Int S = 1;S <= up;++ S) if (S >> id[fa] & 1) chkmin (f[u][c],g[S][c] + h[up ^ S]);
		}
		for (Int v : G[u]) id[v] = -1,nxtS[v] = 0;
	}
}

signed main(){
	read (n,m,K,s),cnt = n;
	for (Int i = 1;i <= n;++ i) read (c[i]);
	for (Int i = 1,u,v;i <= m;++ i) read (u,v),add_edge (u,v);
	Tarjan (1,0),memset (id,-1,sizeof (id)),dfs (1,0);
	int ans = inf;
	for (Int i = 1;i <= K;++ i) chkmin (ans,f[1][i]);
	write (ans),putchar ('\n');
	return 0;
}

T3

Description

有一个长度为 \(n\) 的序列 \(a_{1,2,...,n}\),对于每一个子串 \([l,r]\),称函数 \(f\) 合法当且仅当 \(f:A\to A\),其中 \(A=\{1,2,...,m\}\),且 \(f(a_{l+i-1})=a_{r-i+1}\)

问所有子串的合法函数之和,对 \(998244353\) 取模。

Solution

可以看出的是,如果一个区间 \([l,r]\) 存在合法函数,那么答案就是 \(m^{m-t}\),其中 \(t\) 表示不同 \(a_i\) 个数。

然后我们发现实际上一个区间 \([l,r]\) 合法当且仅当它本质上是一个回文串,本质是回文串当且仅当相对位置回文。那么我们设 \(nxt_i\) 表示与 \(a_i\) 相同的下一个位置,\(pre_i\) 表示与 \(a_i\) 相同的上一个位置,那么从 \([l+1,r-1]\) 能推出 \([l,r]\) 也合法当且仅当 \(nxt_l>r\) 或者 \(a_{l+r-nxt_l}=a_r\) 并且 \(pre_r<l\) 或者 \(a_{l+r-pre_r}=a_l\)

然后你发现可以直接马拉车。复杂度 \(\Theta(n)\)

Code

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

#define Int register int
#define mod 998244353
#define MAXN 600005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,m,mid,maxr,a[MAXN],pw[MAXN],mp[MAXN],nxt[MAXN],pre[MAXN],sum[MAXN],cnt[MAXN],lst[MAXN],len[MAXN];

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);}

signed main(){
	read (n,m),pw[0] = 1;
	for (Int i = 1;i <= m;++ i) pw[i] = mul (pw[i - 1],m);
	for (Int i = 1;i <= n;++ i) read (a[i]);
	for (Int i = 1;i <= n;++ i) pre[i] = lst[a[i]],lst[a[i]] = i;
	for (Int i = 1;i <= m;++ i) lst[i] = n + 1;
	for (Int i = n;i >= 1;-- i) nxt[i] = lst[a[i]],lst[a[i]] = i;
	int ans = 0;
	for (Int i = 1;i < 2 * n;++ i){
		int l,r;
		if (i < maxr){
			int c = 2 * mid - i;
			l = (c - len[c]) / 2 + 1,r = (c + len[c]) / 2,sum[i] = sum[c],cnt[i] = cnt[c];
			while (r - l + 1 > maxr - i) Sub (sum[i],pw[m - cnt[i]]),cnt[i] -= nxt[l ++] > r,cnt[i] -= pre[r --] < l;
			l += (i - c) / 2,r += (i - c) / 2; 
		}
		else{
			if (i & 1) l = r = (i + 1) / 2,cnt[i] = 1;
			else l = i / 2,r = l + 1,cnt[i] = 1 + (a[l] != a[r]);
			sum[i] = pw[m - cnt[i]];
		}
		while (l > 1 && r < n && (nxt[l - 1] > r || a[i + 1 - nxt[l - 1]] == a[r + 1]) && (pre[r + 1] < l || (a[i + 1 - pre[r + 1]] == a[l - 1])))
			cnt[i] += nxt[-- l] > r,cnt[i] += pre[++ r] < l,Add (sum[i],pw[m - cnt[i]]);
		len[i] = r - l + 1,Add (ans,sum[i]);if (maxr <= i + len[i]) maxr = i + len[i],mid = i;
	} 
	write (ans),putchar ('\n');
	return 0;
}

posted @ 2021-05-27 22:17  Dark_Romance  阅读(55)  评论(0编辑  收藏  举报