2021-10-21 集训补题

CF848D Shake It!

link

Solution

可以想到我们可以设 \(f_{i,j}\) 表示增加了 \(i\) 个点的图最小割为 \(j\) 的方案数。然后你发现如果把一条边 \((u,v)\) 增加了 \((u,w),(w,v)\) 那么,\((u,w)\) 和 \((w,v)\) 就会又拓展出一个子问题,这两个子问题最小割取 \(\min\),然后拓展出的其他边(比如 \((u,w_1),(w_1,v),(u,w_2),(w_2,v),....\))这些最小割都需要加起来,然后还需要把 \((u,v)\) 切掉,所以还要加 \(1\) 。

但是我们要求本质不同,所以问题就很麻烦了。为了方便,我们可以先算出一条边拓展出来的方案数,即 \(g_{i,j}\),表示假设拓展了一个 \((u,w),(w,v)\) 出来它点数为 \(i\) ,最小割为 \(j\) 的方案数。这个直接 \(f_i\) 做后缀和再卷积一下再做后缀差分就好了。

然后你发现我们如果要本质不同的话,我们可以对于每个状态枚举选了多少个。但是你发现不太好选,实际上可以枚举 \(a,b\) ,然后用插板就可以确定每个状态选了多少个,所以这个就可以直接组合数计算了。

Code

#include <bits/stdc++.h>>
using namespace std;
 
#define Int register int
#define mod 1000000007
#define MAXN 105

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,fac[MAXN],ifac[MAXN],f[MAXN][MAXN],g[MAXN][MAXN],F[MAXN][MAXN],h[MAXN][MAXN],dp[MAXN][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;}
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 calc (int i1,int j1,int a,int b){
	int cur = 1,ans = 0;
	for (Int i = 0;i * a <= i1 && i * b <= j1;++ i) Add (ans,mul (ifac[i],mul (cur,dp[i1 - i * a][j1 - i * b]))),cur = mul (cur,add (g[a][b],i));
	return ans;
}

signed main(){
	read (n,m),f[0][1] = dp[0][0] = 1;
	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 <= n;++ i){
		for (Int j = n + 1;j >= 1;-- j) F[i - 1][j] = add (F[i - 1][j + 1],f[i - 1][j]);
		for (Int j = 1;j <= n + 1;++ j){
			h[i][j] = 0;
			for (Int k = 0;k < i;++ k) Add (h[i][j],mul (F[k][j],F[i - k - 1][j]));
		}
		for (Int j = n + 1;j >= 1;-- j) g[i][j] = dec (h[i][j],h[i][j + 1]);
		for (Int j = 1;j <= n + 1;++ j)
			for (Int x = n + 1;x >= 0;-- x)
				for (Int y = n + 1;y >= 0;-- y) dp[x][y] = calc (x,y,i,j);
		for (Int j = 1;j <= n + 1;++ j) f[i][j] = dp[i][j - 1];
	}
	write (f[n][m]),putchar ('\n');
	return 0;
}

Yuezheng Ling and Dynamic Tree

link

Solution

我觉得做这个题目就应该时刻谨记我们是在序列上进行操作的才可能做出来。

我们可以考虑分块。对于一个块里面的每个点我们考虑维护出,它第一个不在这个块内的祖先。考虑修改操作,散块可以直接修改然后重构。对于整块,我们可以打懒标记。你发现在操作次数 \(\ge \sqrt n\) 的时候,它里面所有点的父亲都一定不在块内了,就可以直接修改,否则我们还是暴力重构。这部分的复杂度是 \(\Theta(n\sqrt n)\)。

考虑查询,你发现直接像找 lca 一样跳就好了,复杂度就是块数,即 \(\Theta(q\sqrt n)\) 。

Code

#include <bits/stdc++.h>
using namespace std;
 
#define Int register int
#define MAXN 100015 
 
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,q,siz,cnt,a[MAXN],b[MAXN],tag[MAXN],col[MAXN],cor[MAXN],bel[MAXN],tot[MAXN];

void modify (int x){
	for (Int i = col[x];i <= cor[x];++ i) a[i] = max (a[i] - tag[x],1);tag[x] = 0;
	for (Int i = col[x];i <= cor[x];++ i) if (a[i] < col[x]) b[i] = a[i];else b[i] = b[a[i]];
}

void change (int l,int r,int x){
	int qL = bel[l],qR = bel[r];
	if (qL == qR){
		for (Int i = l;i <= r;++ i) a[i] = max (a[i] - x,1);
		return modify (qL);
	}
	else{
		for (Int i = l;i <= cor[qL];++ i) a[i] = max (a[i] - x,1);modify (qL);
		for (Int i = col[qR];i <= r;++ i) a[i] = max (a[i] - x,1);modify (qR);
		for (Int i = qL + 1;i <= qR - 1;++ i){
			tot[i] ++,tag[i] = min (n,tag[i] + x);
			if (tot[i] <= siz) modify (i);
		}
	}
}

int getlst (int x){return max (1,b[x] - tag[bel[x]]);}

int query (int x,int y){
	while (1){
		if (bel[x] < bel[y]) swap (x,y);
		if (bel[x] != bel[y]) x = getlst (x);
		else{
			if (getlst (x) != getlst (y)) x = getlst (x),y = getlst (y);
			else break;
		}
	}
	while (x != y){
		if (x < y) swap (x,y);
		x = max (1,a[x] - tag[bel[x]]);
	}
	return x;
}

signed main(){
	read (n,q),siz = sqrt (n),cnt = (n - 1) / siz + 1,bel[1] = 1;
	for (Int i = 2;i <= n;++ i) read (a[i]),bel[i] = (i - 1) / siz + 1;
	for (Int i = 1;i <= cnt;++ i) col[i] = (i - 1) * siz + 1,cor[i] = min (n,i * siz);
	for (Int i = 1;i <= cnt;++ i) modify (i);
	while (q --> 0){
		int opt,l,r,x;read (opt,l,r);
		if (opt == 1) read (x),change (l,r,x);
		else write (query (l,r)),putchar ('\n');
	}
	return 0;
}
上一篇:NOIP 模拟 七十八


下一篇:[CSP-S2020] 函数调用 & 贪吃蛇