2022/3/3 集训题解
sequence
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
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
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;
}