20210817 noip42

考场

第一眼判断出了四道题的算法,然而并没有什么卵用

T1 套路题
T2 T3 暴力 DP
T4 疯狂码了 3h kmp+manacher,代码又臭又长,最后还是挂了

res

rk6 100+30+40+25
T4 没过拍,数组就没开大,结果发现它能过 50pts???

rk1 张泽阳 100+100+10+0
rk1 yzf 100+100+10+0
rk3 zkx 100+100+0+0

顺便一提,这次的数据就是搞笑的,T4 \(|S|=500\) 就过不了拍的程序过了 \(|S|\le2\times10^5\),还是 subtask。
T1 保证 \(w_i\) 随机,结果最后俩 subtask \(w_i\in{0,1}\),ycx 精准特判 A 了

sol

T1

取对数

考场代码

const int N = 2e5+5, mod = 1e9+7;
int n,w[N];
vector<int> to[N];

double val[N],f[N][2];
vector<int> pre[N][2][2];

void dfs(int u,int fa) {
	f[u][1] = val[u];
	for(int v : to[u]) if( v != fa ) {
		dfs(v,u);
		f[u][1] += f[v][0], pre[u][1][0].pb(v);
		if( f[v][0] < f[v][1] ) f[u][0] += f[v][1], pre[u][0][1].pb(v);
		else f[u][0] += f[v][0], pre[u][0][0].pb(v);
	}
}
LL calc(int u,bool i) {
	LL res = 1;
	if( i ) res = w[u];
	for(int v : pre[u][i][0]) res = res * calc(v,0) %mod;
	for(int v : pre[u][i][1]) res = res * calc(v,1) %mod;
	return res;
}

signed main() {
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	read(n);
	For(i,1,n) read(w[i]), val[i] = log(w[i]);
	For(i,1,n-1) { int x,y; read(x,y); to[x].pb(y), to[y].pb(x); }
	dfs(1,0);
	write(calc(1,f[1][0]<f[1][1]));
	return iocl();
}
T2

每个形如 \(2^ix\) 的极长数列,相邻两项必然在不同集合中。这样的数列之后 \(\log n\) 种长度,且只有长度为奇数的会对两个集合造成不同影响,算出长度为奇数的数量后组合数统计答案。

const int mod = 10000019;
LL n,m;
int q;

LL po,cnt[2],fac[mod],inv[mod];

LL Pow(LL x,LL y=mod-2)
	{ LL res=1; for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod; return res; }
LL C(LL n,LL m) { return n<m ? 0 : fac[n]*inv[n-m]%mod*inv[m]%mod; }
LL lucas(LL n,LL m) {
	if( n < m || m < 0 ) return 0;
	if( !m ) return 1;
	return C(n%mod,m%mod) * lucas(n/mod,m/mod) %mod;
}

signed main() {
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	fac[0] = 1;
	For(i,1,mod-1) fac[i] = fac[i-1] * i %mod;
	inv[mod-1] = Pow(fac[mod-1]);
	rFor(i,mod-2,0) inv[i] = (i+1) * inv[i+1] %mod;
	read(n,q);
	For(i,0,60) {
		LL l = n/(1ll<<i+1), r = n/(1ll<<i);
		cnt[!(i&1)] += (r-l)/2 + ((l+1&1)&&(r&1));
	}
//	cerr<<cnt[0]<<' '<<cnt[1]<<endl;
	po = Pow(2,cnt[0]);
	while( q-- ) {
		read(m);
		write(po*lucas(cnt[1],m-(n-cnt[1])/2)%mod);
	}
	return iocl();
}

T3

考虑两种计算整数划分的 DP:\(f[i,j]\) 表示最后一组大小为 \(i\),总和为 \(j\)\(g[i,j]\) 表示分成 \(i\) 组,总和为 \(j\)。分块使用两种 DP,这样第一维大小变为 \(\sqrt n\),统计答案时合并即可。

const int N = 1e5+5, B = sqrt(1e5);
int mn,mx,n,mod;

int f[N],g[N],g1[N];

int work(int x) {
	mem(f,0,n), mem(g,0,n), mem(g1,0,n);
	int b = max(x,B);
	f[0] = g[0] = g1[0] = 1;
	For(i,x,b-1) For(j,i,n) f[j] = (f[j] + f[j-i]) %mod;
	For(i,1,n/b) {
		int del = i*b;
		For(j,i,n-del) g[j] = (g[j] + g[j-i]) %mod;
		For(j,0,n-del) g1[j+del] = (g1[j+del] + g[j]) %mod;
	}
	int res = 0;
	For(i,0,n) res = (res + (LL)f[i] * g1[n-i]) %mod;
	return res;
}

signed main() {
	read(mn,mx,n,mod);
	write((work(mn)-work(mx+1)+mod)%mod);
	return iocl();
}
T4

先把两端相同的去掉,变为 \(A+B+C+D\) 的形式。manacher 预处理回文半径,枚举回文中心(注意分类讨论在 \(A\) 中还是在 \(C\) 中),kmp 判断是否合法(有些不一定合法的情况没有判是因为它们一定不优,对答案没有影响)。

直接在考场代码上改的,比较丑陋

const int N = 1e7+5;
char s[N];

int n,cut,m,ans,f[N],kmp[N],pre[N];
char a[N],b[N];

void work() {
//	For(i,1,n) cerr<<s[i]; cerr<<endl;
	rFor(i,n,1) a[i*2] = s[i], a[i*2-1] = '$'; a[0] = a[ m=n*2+1 ] = '$';
	For(i,1,n) b[i] = s[i], b[n+i+1] = s[n-i+1]; b[n+1] = '$';
	for(int i = 1, mid = 0, r = 0; i <= m; ++i) {
		int g = 0;
		if( i <= r ) g = min(f[mid*2-i],r-i);
		while( a[i-g-1] == a[i+g+1] ) ++g;
		if( i+g > r ) mid = i, r = i+g;
		f[i] = g;
	}
//	For(i,1,m) cerr<<f[i]<<' '; cerr<<endl;
	for(int i = 2, j = 0; i <= m; ++i) {
		while( j && b[j+1] != b[i] ) j = kmp[j];
		if( b[j+1] == b[i] ) ++j;
		kmp[i] = j;
	}
//	For(i,1,m) cerr<<kmp[i]<<' '; cerr<<endl;
	For(i,1,m) {
		int l1 = 1, r1 = i/2-(f[i]+1)/2, l2 = (i+1)/2+(f[i]+1)/2, r2 = n;
//		assert(r1==(i-f[i]-1)/2), assert(l2==(i+f[i]+1)/2);
		if( l1 > r1 || l2 > r2 ) { ckmax(ans,f[i]); continue; }
		l2 = n+1+n-l2+1, r2 = n+1+n-r2+1, swap(l2,r2);
		int len = kmp[r2];
		if( len <= r1 ) ckmax(ans,f[i]+len*2);
	}
	For(i,n+1,m) pre[i] = max(pre[i-1],kmp[i]);
	For(i,1,m) {
		int r1 = i/2-(f[i]+1)/2, r2 = n+1+n-((i+1)/2+(f[i]+1)/2)+1;
		if( pre[r2] >= r1 ) {
			ckmax(ans,f[i]+r1*2);
//			cerr<<i<<':'<<f[i]<<' '<<r1<<endl;
		}
	}
}

signed main() {
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	scanf("%s",s+1); n = strlen(s+1);
	while( cut < n/2 && s[1+cut] == s[n-cut] ) ++cut;
	if( cut == n/2 ) { write(n); return iocl(); }
	n -= cut*2;
	For(i,1,n) s[i] = s[i+cut];
	work();
	reverse(s+1,s+n+1), work();
	write(cut*2+ans);
	return iocl();
}
posted @ 2021-08-18 21:29  401rk8  阅读(51)  评论(0编辑  收藏  举报