D - Median of Medians
老经典题了 /se
考虑二分答案,判断比它小的中位数个数。对于当前的 (mid=x) ,可以将原序列所有数看成 (1) 和 (-1) ,统计就用 BIT,每次累加所有之前不大于它的前缀和即可(假设当前是 (sum) ,之前所有 (<sum) 的区间和它作差得到的也不小于 (0))。
bool Check( int x )
{
memset( tr,0,sizeof(tr) );
int sum=0; ll res=0,all=1ll*n*(n+1)/2;
for ( int i=1; i<=n; i++ )
{
Add(ADD+sum,1);
(a[i]>x) ? sum-- : sum++;
res+=Query(ADD+sum-1);
}
return res>=all/2+1;
}
E - Ribbons on Tree
“覆盖所有路径” 很难处理,如果下放到所有子树,其实就是在匹配的时候不能有除根以外的子树 (u) 自匹配(这样就没有到 (fa[u]) 的边)。
设 (dp[u][i]) 表示子树 (u) 中还有 (i) 个没有匹配的合法方案数。易得 (dp[u][i+j]=sum dp[u][i] imes dp[v][j]) ,注意 (dp[u][0]) 要去掉自匹配的方案数,即 (dp[u][0]-=sum dp[u][i] imes self[i]) .
注意因为 DFS 的时候, (dp[u][0]) 容斥系数是取反的,所以在输出 (dp[1][0]) 的时候要取负。
//Author: RingweEH
void Adde( int u,int v ) { e[++tot].to=v; e[tot].nxt=head[u]; head[u]=tot; }
void bmod( int &x,int y ) { y+=(y<0)*Mod; x+=y-Mod; x+=(x<0)*Mod; }
void DFS( int u,int fat )
{
siz[u]=1; dp[u][1]=1;
for ( int i=head[u]; i; i=e[i].nxt )
{
int v=e[i].to; if ( v==fat ) continue;
DFS(v,u);
for ( int x=0; x<=siz[u]+siz[v]; x++ ) g[x]=0;
for ( int x=0; x<=siz[u]; x++ )
for ( int y=0; y<=siz[v]; y++ )
bmod(g[x+y],1ll*dp[u][x]*dp[v][y]%Mod);
for ( int x=0; x<=siz[u]+siz[v]; x++ ) dp[u][x]=g[x];
siz[u]+=siz[v];
}
for ( int i=2; i<=siz[u]; i+=2 ) bmod(dp[u][0],Mod-1ll*dp[u][i]*self[i]%Mod);
}
int main()
{
n=read();
for ( int i=1,u,v; i<n; i++ ) u=read(),v=read(),Adde(u,v),Adde(v,u);
self[0]=1;
for ( int i=2; i<=n; i+=2 ) self[i]=1ll*self[i-2]*(i-1)%Mod;
DFS(1,0);
printf("%d
",Mod-dp[1][0] );
return 0;
}
F - Robots and Exits
首先,显然只有一个选择的机器人无效,直接删除。令每个机器人到左边第一个出口的距离为 (x_i) ,到右边的距离为 (y_i) . 每次移动相当于把 ((x,y)) 变成 ((x,y+1),(x+1,y)) ,(x=a) 时就从左边出口出去,反之亦然。
那么可以转化为从原点开始向上或者向右走,那么折线两边的点集就是走左边或者右边的点集,两个方案不同当且仅当某一边的点集不同。
不妨令折线为下点集的轮廓,设 (dp[i]) 为折线最后一个点为 (i) 的方案数,有
[dp[i]=1+sum_{x_j<x_i,y_j<y_i}dp[j]
]
BIT 优化即可,按照 (x) 升序,因为是严格 (<) 所以还要 (y) 降序。
//Author: RingweEH
int main()
{
n=read(); m=read();
for ( int i=1; i<=n; i++ ) a[i]=read();
for ( int i=1; i<=m; i++ ) b[i]=read();
for ( int i=1; i<=n; i++ )
{
if ( a[i]<=b[1] || a[i]>=b[m] ) continue;
int x=lower_bound(b+1,b+1+m,a[i])-b;
if ( b[x]==a[i] ) continue;
p[++cnt]=make_pair(a[i]-b[x-1],b[x]-a[i]); tmp[cnt]=b[x]-a[i];
}
sort(tmp+1,tmp+1+cnt); tot=unique(tmp+1,tmp+1+cnt)-tmp-1;
for ( int i=1; i<=cnt; i++ )
p[i].se=lower_bound(tmp+1,tmp+1+tot,p[i].se)-tmp;
sort(p+1,p+1+cnt,cmp); cnt=unique(p+1,p+1+cnt)-p-1;
dp[0]=1;
for ( int i=1; i<=cnt; i++ )
dp[i]=Query(p[i].se-1)+1,Add(p[i].se,dp[i]);
int ans=0;
for ( int i=0; i<=cnt; i++ ) ans=(ans+dp[i])%Mod;
printf("%d
",ans);
return 0;
}