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();
}