20210719 noip20

考场

后两题是原题,教练说不用写了(ycx 不讲武德)

T1 先手模了 \(n\le5\) 的情况,尝试找规律失败。那就只能 DP 了,最终没搞出来。

记忆化搜索打了 \(n\le20\) 的表,交了

res

30pts

少模了,爆了 20pts。。。

rk1 赵思远 100+81+45
rk4 ys 30+72+65

玩具

具体做法可以看hzoi巨佬的博客(虽然有锅)

这里补充一下细节和如何想到。(下文中高度 = 深度,按边数计)

显然题意为求 \(n\) 个点的树树高期望
一个 naive 的想法设状态为 \(f[i,j]\) 表示 \(i\) 个点的树树高为 \(j\) 的概率,但直接转移需要知道每个深度度有多少个点,显然不行。那么考虑有若干个树加一个根拼起来,设 \(g[i,j]\)\(i\) 个点的森林深度为 \(j\) 的概率,那么 \(f[i,j]=g[i-1,j-1]\),问题转化为求 \(g\)
考虑枚举第一棵树中有多少点,那么可以由第一棵树+其他点的森林转移而来,但这样还要枚举深度,于是转换第二维为树高小于等于 \(j\) 的概率,\(g[i,j]=\sum_{k=1}^if[k,j]\times g[i-k,j]\),这样还少了一部分,即第一棵树有 \(k\) 个点的概率,设为 \(dp\),应再乘上 \(dp[i,k]\)
最后的问题就是求 \(dp\),对于第 \(i\) 个点,它有 \(i\) 种选择(接在前 \(i-1\) 个点后或新开一棵树),则有 \(dp[i,j]=dp[i-1,j-1]\times\frac{j-1}i+dp[i-1,j]\times\frac{i-j}i\)

带注释的 code
const int N = 205;
int n,mod;

LL ans,inv[N],dp[N][N],f[N][N],g[N][N];

LL Pow(LL x,LL y)
	{ LL res=1; for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod; return res; }

signed main() {
	read(n,mod);
	For(i,1,n) inv[i] = Pow(i,mod-2);
	dp[1][1] = 1;
	For(i,2,n) For(j,1,i)
		dp[i][j] = (dp[i-1][j-1]*(j-1) + dp[i-1][j]*(i-j))%mod*inv[i]%mod;
	f[1][0] = 1; // 单点的深度为0
	For(i,0,n) g[0][i] = 1;
	For(i,1,n) For(j,0,n) { // j要循环到n,因为g[i][j]=1(j>i)
		if( j ) f[i][j] = g[i-1][j-1];
		For(k,1,i) g[i][j] = (g[i][j] + dp[i][k]*f[k][j]%mod*g[i-k][j]) %mod;
	}
	For(i,1,n) ans = (ans + (f[n][i]-f[n][i-1])*i) %mod;
	// f[n,i]-f[n,i-1]得到深度为i的概率
	write((ans%mod+mod)%mod);
	return iocl();
}

y

状压 DP + meet in the middle

bitset
const int N = 95;
int n,m,d;
bitset<N> w[2][N];

int l,ans;
bitset<N> f[1<<12],g[1<<12];
// f[s][i]:从1开始路径为s到i的合法性; g:从2..n开始

signed main() {
	read(n,m,d); l = d/2;
	For(i,1,m) {
		int x,y,z; read(x,y,z);
		w[z][x][y] = w[z][y][x] = 1;
	}
	f[1][1] = 1;
	For(i,2,n) g[1][i] = 1;
	for(int i = 1; i < 1<<l; ++i)
		For(j,1,n) if( f[i][j] ) f[i<<1] |= w[0][j], f[i<<1|1] |= w[1][j];
	for(int i = 1; i < 1<<d-l; ++i)
		For(j,1,n) if( g[i][j] ) g[i<<1] |= w[0][j], g[i<<1|1] |= w[1][j];
	for(int i = 1<<l; i < 1<<l+1; ++i) for(int j = 1<<d-l; j < 1<<d-l+1; ++j)
		For(k,1,n) if( f[i][k] && g[j][k] ) { ++ans; break; }
	write(ans);
	return iocl();
}

z

先咕了

upd 20210902:
核心:离线,按长度递减的顺序处理询问,同时删除位移绝对值比 \(l\) 小的任务。
代码极其恶心,写的我身心俱疲。

带注释的 code
typedef pair<LL,LL> PLL;

const int N = 1e5+5;
int n,q,x[N];
PII l[N];

int m;
LL ans[N]; // ans[0]: l=0的答案
priority_queue<PLL,vector<PLL>,greater<PLL>> pq;
map<LL,LL> mp;

LL calc(LL l) { return ans[0] - (mp.size()-(!mp.empty()&&mp.begin()->se<0)) * l; }
// l<min{x}: 直接算答案(每次可以减少移动距离l,但第一个任务需要向左时不能)

signed main() {
	read(n,q);
	For(i,1,n, lst=0) {
		int y; read(y); if( y == lst ) continue;
		if( m && ((x[m]<0&&y<lst) || (x[m]>0&&y>lst)) ) x[m] += y-lst;
		else x[++m] = y-lst;
		lst = y;
	} // x转化为位移
	For(i,1,q) read(l[i].fi), l[i].se = i; sort(l+1,l+q+1);
	For(i,1,m) {
		ans[0] += abs(x[i]), mp[i] = x[i];
		pq.push(MP(abs(x[i]),i));
	}
	LL t = 1;
	while( !pq.empty() ) {
		PII u = pq.top(); pq.pop(); auto it = mp.lower_bound(u.se);
		if( it == mp.end() || it->fi != u.se || abs(it->se) != u.fi ) continue;
		while( t<=q && abs(it->se) > l[t].fi ) ans[l[t].se] = calc(l[t].fi), ++t;
		// 剩余的l都比最小位移绝对值大,尝试删除
		if( it != mp.begin() ) {
			if( it != prev(mp.end()) ) { // 三合一
				auto pre = prev(it), suf = next(it);
				ans[0] -= abs(it->se) + abs(pre->se) + abs(suf->se);
				it->se += pre->se + suf->se;
				ans[0] += abs(it->se);
				pq.push(MP(abs(it->se),u.se)), mp.erase(pre), mp.erase(suf);
			} else ans[0] -= abs(it->se), mp.erase(it);
		} else {
			if( it->se > 0 ) { // 向右时可以被l顺便覆盖
				if( it != prev(mp.end()) ) { // 二合一
					auto suf = next(it);
					ans[0] -= abs(it->se) + abs(suf->se);
					it->se += suf->se;
					if( it->se ) {
						ans[0] += abs(it->se);
						pq.push(MP(abs(it->se),u.se));
					} else mp.erase(it);
					mp.erase(suf);
				} else ans[0] -= abs(it->se), mp.erase(it);
			}
		}
	}
	while( t<=q ) ans[l[t].se] = calc(l[t].fi), ++t;
	For(i,1,q) write(ans[i]);
	return iocl();
}
posted @ 2021-07-20 09:31  401rk8  阅读(48)  评论(1编辑  收藏  举报