苹果又来讲课的记录

https://www.cnblogs.com/xingyuxuan/p/18282873

太困难。

image

苹果的 dp 划分:带环转移和不带环转移,不带环转移分为区间 dp 和顺序 dp。

吃鱼问题

\(n\) 条鱼,每条鱼有大小 \(a_i\),一个鱼可以吃旁边的鱼(吃完之后新的接上来),如果 \(a_i\ge a_j\) 就直接吃,否则需要 \(1\) 点能量。

问最后 \(i=1,2,\dots,n\) 吃掉所有鱼的最小能量。\(n\le 10^6\)

Solution:

比较容易的是 \(O(n^2)\) 的区间 dp。转移是能不能吃掉左右的鱼,最后求 \(f(i,i)\)

观察是 \(f(l,r)=O(\log nV)\) 因为翻倍性质。进行换维,\(f(l,x)\) 表示 \(f(l,r)=x\) 的最大 \(r\)

考虑转移。扫描线 \(l\)\(l\) 转移是 \(f(l+1,r)=f(l,r)+[s_r-s_l\le a_l]\),这个相当于加了一个断点。

然后 \(r\) 转移也是加(值域)断点。最后求答案相当于把这些区间每次搞个区间 \(\max\)。怎么不在 \(n\log n\) 个区间上又带个 \(\log\)??方法是在 ST 表上打上那两个区间的 tag,最后 dfs 一下 ST 表下放标记即可。

据说可以线性。

一个扩展是 \(a_i\ge a_j+k\) 才能吃。这个翻倍性质变为 \(sum-k\) 翻倍。但是此时注意到 \(s_r-s_{l-1}\) 的区间有 \(f(l,r)=r-l+1\) 所以也可以做。

UOJ 主旋律

统计 DAG、强连通图一类的方法:

DAG 子图(边):

钦定一个集合 \(T\subset S\) 作为零入度点,然后进行计算。

\[f_S=\sum_{T\subset S}(-1)^{|T|+1}f(S-T)g(T,S-T) \]

这里的 \(g(A,B)\)\(2\)\(A\)\(B\) 连边的次方。苹果说这里的重要是每个图被统计了 \(1\) 遍(但是这好像就是容斥原理。。),这是因为

\[\sum_{i\ge 1}\binom mi (-1)^{i+1}=1 \]

强连通图大致相同,比如这道:

钦定一个集合 \(T\subset S\) 表示在缩点后的零入度点的点的集合。这个时候剩下的 \(S-T\)\(T\to S-T\) 的边可以随意定向不影响 \(T\) 的性质。但是 \(T\) 内部不能有环。那么只需要设 \(g_T\) 是这样 \(T\)(独立于原图)的方案数。接下来我容斥 \(T\):钦定强连通分量,如果有 \(c\) 个那么获得 \((-1)^{c+1}\) 的系数。

转移 \(g\) 即枚举一个强连通分量集合乘上 \(f\)\(f\) 不满足是强连通分量性质,所以要容斥),保证唯一性是要包含 lowbit。因为增加了强连通分量需要乘上 \(-1\)。先转移 \(g\)

这里有一个问题:我目前没有/没法计算 \(g\) 只有一个强连通分量转移过来的情况(因为这依赖 \(f_S\))。但是恰好这样的情况无需考虑;这只会干扰 \(g_S\to f_S\) 的情况,而这种情况恰好只有一个强连通分量,是被 ban 掉的。最后才加上修正 \(g_S\gets g_S+f_S\) 即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int f[(1<<15)+5],g[(1<<15)+5],n,m,a[16][16];
bitset<210> ed[(1<<15)+5][2];
int geted(int S,int T){
	return (ed[S][0]&ed[T][1]).count();
}
int pw[40430];
signed main(){
	pw[0]=1;
	for(int i=1;i<=2192;i++)pw[i]=pw[i-1]*2%mod;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;cin>>x>>y;
		a[x][y]=1;
		for(int j=0;j<(1<<n);j++){
			if(j&(1<<x-1))ed[j][0][i]=1;
			if(j&(1<<y-1))ed[j][1][i]=1;
		}
	}
	g[0]=mod-1,f[0]=1;
	for(int S=1;S<(1<<n);S++){
		int t=S&(-S);
		for(int T=(S-1)&S;T;T=(T-1)&S)if(T&t)(g[S]+=(mod-1)*f[T]%mod*g[S-T]%mod)%=mod;
		f[S]=pw[geted(S,S)];
		for(int T=S;T;T=(T-1)&S)
			(f[S]+=(mod-1)*g[T]%mod*pw[geted(T,S-T)+geted(S-T,S-T)])%=mod;
		(g[S]+=f[S])%=mod;
	}
	cout<<f[(1<<n)-1]<<endl;
	return 0;
} 

Calculate Cactus

(边)子图边仙人掌计数,有点 🐒。

MEX counting

\(f(i,j,k)\) 是考虑了 \(1\sim i\)\(mex=j\),有 \(k\) 种数 \(>mex+1\) 的。

这样的状态能施行是因为钦定了 \(i,j,k\) 一样的所有情况答案相等。因为转移,这些状态确实相等。

然后列出转移式最重要的情况就是加入了 \(j\)

对于一个 \(i\),设这个时候的辅助数组 \(g_{j,k}\),我想干的事情是多步换成单步。由于我们的钦定,考虑 bitmask:可能从每个扩张 mex 的方案转移过来,也就是说,单步的 \((j,k)\)\(k'+1\) 种方案从 \((j-1,k'+1)\) 的状态转移过来。

还有一种方法是直接考虑多步然后前缀和,但是泛用性没有辅助数组强。

苹果:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll N = 2000 + 5, Mod = 998244353;
ll n = 0, k = 0, b[N] = {}, p = 0, A[N][N] = {};
ll dp[N][N] = {}, g[N][N] = {}, ans = 0; 

int main(){
	scanf("%lld %lld", &n, &k);
	for(ll i = 1 ; i <= n ; i ++) scanf("%lld", &b[i]);
	dp[0][0] = 1;
	for(ll i = 0 ; i <= n ; i ++){
		A[0][i] = 1;
		for(ll j = 1 ; j <= i ; j ++) A[j][i] = A[j - 1][i] * (i - j + 1) % Mod;
	}
	for(ll i = 1 ; i <= n ; i ++){
		for(ll mex = max(1ll, p) ; mex <= min(b[i] + k, n) ; mex ++) for(ll j = 0 ; j <= i ; j ++) g[j][mex] = (dp[j][mex - 1] + g[j + 1][mex - 1] * (j + 1)) % Mod;
		for(ll j = 0 ; j <= i ; j ++) for(ll mex = p ; mex <= min(b[i] + k, n) ; mex ++){
			if(j) g[j][mex] = (g[j][mex] + dp[j - 1][mex]) % Mod;
			g[j][mex] = (g[j][mex] + dp[j][mex] * (j + mex)) % Mod;
		}
		for(ll j = 0 ; j <= i ; j ++) for(ll mex = p ; mex <= min(b[i - 1] + k, n) ; mex ++) dp[j][mex] = 0;
		for(ll j = 0 ; j <= i ; j ++) for(ll mex = max(p, b[i] - k) ; mex <= min(b[i] + k, n) ; mex ++) dp[j][mex] = g[j][mex];
		for(ll j = 0 ; j <= i ; j ++) for(ll mex = p ; mex <= min(b[i] + k, n) ; mex ++) g[j][mex] = 0;
		p = max(p, b[i] - k);
	}
	for(ll j = 0 ; j <= n ; j ++) for(ll mex = p ; mex <= min(b[n] + k, n) ; mex ++) ans = (ans + dp[j][mex] * A[j][n - mex]) % Mod;
	printf("%lld", ans);
	return 0;
}

(min,+) 卷积

凸包+凸包:闵可夫斯基和

凸包+匪徒包:SMAWK/决策单调性分治

匪徒包+匪徒包(随机):取前 \(n\log n\) 小的 \(a_i+b_j\),然后把没被更新的位置暴力跑。

背包

01 背包的牛逼人士复杂度 \(O(nv^2/nw^2)\)

方法是:按大小加入,每次把当前的 \(f\)\(\bmod i\) 分组,然后我们搞得是凸包加匪徒包,SMAWK 就可以做到 \(O(nv^2)\)

完全背包的牛逼人士复杂度 \(O(w^2\log W)\)\(w\) 是一个的重量的 max,\(W\) 是总重量。

方法是:完全背包无限制,所以可分开体积 \(f(x+y)\gets f(x)+f(y)\)

每次计算 \(mid-w/2,\dots,mid+1,mid+2,\dots mid+w/2\),这样下去。这正确的原因是把物品放出来,尽量沿中间剖开,得到的一定在这个区间内。

多重背包的牛逼人士

posted @ 2024-07-25 09:28  British_Union  阅读(7)  评论(0编辑  收藏  举报