Live2D

2022/3/3 集训题解

sequence

link

Solution

先考虑 \(a_{1,2,...,n}\) 存在重复的情况,那么我们可以求出它的最长不重复前缀以及最长不重复后缀,然后我们枚举它出现的位置,考虑判断合法的方案数。那么我们可以设 \(f_{i,x}\) 表示已经添加了 \(i\) 个,到当前有 \(x\) 个互不相同的数的方案数,然后直接来就好了,右边也是一样的,然后直接算就好了。

考虑对于两两不同的情况,我们如何进行处理,这个时候,我们可以枚举左边第一个出现重复的位置,然后跟上面类似,不过需要找到 \(a_{1,2,..,n}\) 第一个与之匹配的位置,所以变成了 \(nk^2\),所以我们用前缀和优化一下就好了。

Code

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

#define Int register int
#define MAXN 25005
#define MAXK 405

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');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

int n,K,m,a[MAXN],pw[MAXN],f[MAXN][MAXK],fac[MAXN],ifac[MAXN];

#define mod 1000000007
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;}
int qkpow (int a,int b){
	int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
	return res;
}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);}

int cnt;
map <int,int> mp;
void add (int v){if (!mp[v] ++) cnt ++;}
void del (int v){if (!-- mp[v]) cnt --;}

int g1[MAXN],g2[MAXN];

void addsum (int x,int l,int r,int v){Add (f[x + 1][l],v),Sub (f[x + 1][r + 1],v);}
int binom (int a,int b){return a >= b ? mul (fac[a],mul (ifac[b],ifac[a - b])) : 0;}

int pre[MAXN][MAXK];

void Workit (){
	f[0][K] = 1;
	for (Int i = 0;i < n - m;++ i){
		f[i + 1][K] = mul (f[i][K],K);
		for (Int j = 0;j < K;++ j) f[i + 1][j] = add (mul (K - j,f[i][j + 1]),pre[i][j]);
		for (Int j = 1;j <= K;++ j) pre[i + 1][j] = add (pre[i + 1][j - 1],f[i + 1][j]);
	}
	int ans = 0;
	for (Int i = 0;i <= n - m;++ i){//nmd i从0开始! 
		int sht = 1,r = n - m - i;
		for (Int j = 1;j <= i && j <= K - m;++ j){
			int res = 0;
			Add (res,mul (pre[i - j][m + j - 1],f[r][K])),
			Add (res,mul (m + j - 1,mul (f[r][m + j - 1],f[i - j][K]))),
			Sub (res,mul (pre[i - j][m + j - 1],f[r][m + j - 1]));
			Add (ans,mul (res,sht)),sht = mul (sht,K - m - j + 1);
		}
		if (i >= K - m) Add (ans,mul (sht,f[n - K][K]));
		else Add (ans,mul (sht,f[r][m + i]));
	}
	write (ans),putchar ('\n');
}

signed main(){
	freopen ("sequence.in","r",stdin);
	freopen ("sequence.out","w",stdout);
	read (n,K,m);
	pw[0] = 1;for (Int i = 1;i <= n - m;++ i) pw[i] = mul (pw[i - 1],K);
	fac[0] = 1;for (Int i = 1;i <= n;++ i) fac[i] = mul (fac[i - 1],i);
	ifac[n] = qkpow (fac[n],mod - 2);for (Int i = n;i;-- i) ifac[i - 1] = mul (ifac[i],i);
	for (Int i = 1;i <= m;++ i) read (a[i]);
	for (Int i = 1;i <= K - 1;++ i) add (a[i]);
	bool flg = 0;
	for (Int i = K;i <= m;++ i){
		add (a[i]);
		if (i > K) del (a[i - K]);
		if (cnt == K){
			flg = 1;
			break;
		}
	}
	if (flg) write (mul (n - m + 1,qkpow (K,n - m))),putchar ('\n');
	else{
		int x = 0;mp.clear();
		for (Int i = 1;i <= m;++ i)
			if (!mp[a[i]]) x = i,mp[a[i]] = 1;
			else break;
		if (x == m){
			Workit ();
			return 0;
		}
		memset (f,0,sizeof (f)),f[0][x] = 1;
		for (Int i = 0;i < n - m;++ i){
			for (Int k = 1;k < K;++ k) if (f[i][k]) addsum (i,1,k,f[i][k]),addsum (i,k + 1,k + 1,mul (K - k,f[i][k]));
			for (Int k = 1;k <= K;++ k) Add (f[i + 1][k],f[i + 1][k - 1]);
		}
		for (Int i = 1;i <= n - m;++ i) Add (g1[i],add (mul (g1[i - 1],K),f[i][K]));
		x = 0,mp.clear();
		for (Int i = m;i >= 1;-- i)
			if (!mp[a[i]]) x = m - i + 1,mp[a[i]] = 1;
			else break;
		memset (f,0,sizeof (f)),f[0][x] = 1;
		for (Int i = 0;i < n - m;++ i){
			for (Int k = 1;k < K;++ k) if (f[i][k]) addsum (i,1,k,f[i][k]),addsum (i,k + 1,k + 1,mul (K - k,f[i][k]));
			for (Int k = 1;k <= K;++ k) Add (f[i + 1][k],f[i + 1][k - 1]);
		}
		for (Int i = 1;i <= n - m;++ i) Add (g2[i],add (mul (g2[i - 1],K),f[i][K]));
		int ans = 0,all = qkpow (K,n - m);
		for (Int i = 0;i <= n - m;++ i) Add (ans,dec (all,mul (dec (pw[i],g1[i]),dec (pw[n - m - i],g2[n - m - i]))));
		write (ans),putchar ('\n');
	}
	return 0;
}

graph

link

Solution

可以看出,当我们找到了一个有趣点之后我们就可以 \(\Theta(n)\) 确定所有有趣点。怎么找有趣点呢?一个点是有趣点的概率为 \(20\%\),所以多枚举几发就好了。

Code

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

#define Int register int
#define MAXN 100005

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');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

int n,m;
vector <int> g[MAXN];

bool vis[MAXN];
int ind,par[MAXN],dfn[MAXN],siz[MAXN];

void dfs (int u){
	vis[u] = 1,siz[u] = 1,dfn[u] = ++ ind;
	for (Int v : g[u]) if (!vis[v]) par[v] = u,dfs (v),siz[u] += siz[v];
}

bool checkin (int x,int y){
	return (dfn[x] <= dfn[y] && dfn[y] <= dfn[x] + siz[x] - 1);
}

bool check (int x){
	dfs (x);
	for (Int u = 1;u <= n;++ u)
		for (Int v : g[u]) if (par[v] != u && !checkin (v,u)) return 0;
	return 1;
}

vector <int> ans;
int tot[MAXN],tov[MAXN];bool sht[MAXN];

void init (int u){
	vis[u] = 1;
	for (Int v : g[u]) if (!vis[v]){
		init (v),tot[u] += tot[v];
		if (tot[v] && tov[v] != u){
			if (!tov[u] || checkin (tov[v],tov[u])) tov[u] = tov[v];
		}
	}
}

void fuckit (int u){
	vis[u] = 1;
	if (tot[u] == 1 && sht[tov[u]]) sht[u] = 1;
	for (Int v : g[u]) if (!vis[v]) fuckit (v);
}

void makeit (int x){
	for (Int i = 1;i <= n;++ i) tot[i] = sht[i] = vis[i] = tov[i] = 0;
	for (Int u = 1;u <= n;++ u)
		for (Int v : g[u]) if (!checkin (u,v)){
			tot[u] ++,tot[v] --;
			if (!tov[u] || checkin (v,tov[u])) tov[u] = v;
		}
	init (x);
	for (Int u = 1;u <= n;++ u) if (tot[u] >= 2) sht[u] = 1;
	for (Int u = 1;u <= n;++ u) vis[u] = 0;
	fuckit (x);
	for (Int u = 1;u <= n;++ u) if (!sht[u] || u == x) ans.push_back (u); 
}

void Work (){
	read (n,m),ans.clear();
	for (Int i = 1;i <= n;++ i) g[i].clear();
	for (Int i = 1,u,v;i <= m;++ i) read (u,v),g[u].push_back (v);
	for (Int tim = 1;tim <= 100;++ tim){
		int x = rand() % n + 1;ind = 0;
		for (Int i = 1;i <= n;++ i) vis[i] = 0;
		if (check (x)){
			makeit (x);
			break;
		}
	}
	for (Int x : ans) write (x),putchar (' ');putchar ('\n');
}

signed main(){
	int T;read (T);srand (time(NULL));
	while (T --> 0) Work ();
	return 0;
}

shape

link

Solution

似乎可以证明的是,我们只需要确定每个向量选多少次,我们就可以确定整个图形。那么我们即是求:

\[\sum_{i=1}^{n} c_i\times x_i=0,\sum_{i=1}^{n} c_i\times y_i=0 \]

\[\sum_{i=1\wedge x_i>0}^{n} c_i\times x_i\le m,\sum_{i=1\wedge y_i>0} c_i\times y_i\le m \]

然后我们考虑对于 \(m\) 的二进制位从小到大考虑,那么你就只需要记录之前的进位,然后枚举 \(c\) 当前二进制位下的选法。

Code

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

#define Int register int
#define MAXN 5

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');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

#define mod 998244353
int n,m,up,dx[MAXN],dy[MAXN],nx[1 << 5],px[1 << 5],ny[1 << 5],py[1 << 5],f[32][MAXN << 2][MAXN << 2][MAXN << 2][MAXN << 2][2][2];

void add (int &a,int b){a += b,a %= mod;}

signed main(){
	read (n,m),f[0][0][0][0][0][0][0] = 1,up = (1 << n) - 1;
	for (Int i = 0;i < n;++ i) read (dx[i],dy[i]);
	for (Int S = 0;S <= up;++ S) for (Int i = 0;i < n;++ i) if (S >> i & 1) (dx[i] > 0 ? nx : px)[S] += abs (dx[i]),(dy[i] > 0 ? ny : py)[S] += abs (dy[i]);
	for (Int t = 0;t <= 30;++ t) 
		for (Int i = 0;i <= nx[up];++ i) for (Int j = 0;j <= px[up];++ j) 
			for (Int k = 0;k <= ny[up];++ k) for (Int h = 0;h <= py[up];++ h)
				for (Int a = 0;a < 2;++ a) for (Int b = 0;b < 2;++ b)
					for (Int S = 0;S <= up;++ S) if (!(i + nx[S] + j + px[S] & 1) && !(k + ny[S] + h + py[S] & 1)){
						int xz = nx[S] + i >> 1,xf = px[S] + j >> 1,yz = ny[S] + k >> 1,yf = py[S] + h >> 1;
						bool A = a + (i + nx[S] & 1) > (m >> t & 1),B = b + (k + ny[S] & 1) > (m >> t & 1);
						add (f[t + 1][xz][xf][yz][yf][A][B],f[t][i][j][k][h][a][b]);
					}
	int ans = f[31][0][0][0][0][0][0];ans --;if (ans < 0) ans += mod;
	write (ans),putchar ('\n');
	return 0;
}
posted @ 2022-03-03 21:51  Dark_Romance  阅读(60)  评论(0编辑  收藏  举报