2021 省选联考 Day 2 意识流题解【倍增 整体二分 状压DP BFS】
宝石
题意
给定一无根树,点有颜色。给定颜色序列 \(P\),无重复数字。\(q\) 次询问 \(u\) 到 \(v\) 路径上的颜色依次排列后,\(P\) 最长的是其子序列前缀。\(n,q\leq 2\times 10^5\),\(m\leq 5\times 10^4\)。
题解
先把颜色按照 \(P\) 中出现顺序重新编号。
预处理每个点开始往上跳 \(2^i\) 种颜色(颜色递增/递减)到哪个点,并求每条路径的起点向上跳到 \(LCA\)(含)最多跳多少种颜色(颜色递增)。整体二分。check 一个答案的时候我们要找到路径终点上方最近的一个颜色为 \(mid\) 的点,看从它向上跳到 \(LCA\)(不含)最多跳多少种颜色(颜色递减),并看起点终点二者拼起来够不够 \(mid\)。
(一个 int
函数没有返回值当场 RE 暴毙)
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;
c=getchar();
}
while(isdigit(c)){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=2e5+10,M=5e4+10,Q=2e5+10,LN=18,LM=16;
#define it vector<int>::iterator
#define forin(i,v) for(it i=v.begin();i!=v.end();++i)
struct bian{
int e,n;
};
int s[N],tot=0;
bian b[N<<1];
void add(int x,int y){
tot++;
b[tot].e=y;
b[tot].n=s[x];
s[x]=tot;
}
int n,m;
int c[N];
int w[N];
int dep[N],f[LN][N];
int g[LM][N],h[LM][N];
int top1[N];
stack<int>stk[M];
vector<int>nod[M];
int dfn[N],dfnend[N],dfnn;
void ss0(int x,int fa){
dfn[x]=dfnend[x]=++dfnn;
stk[w[x]].push(x);
g[0][x]=stk[w[x]+1].top();
h[0][x]=stk[w[x]-1].top();
top1[x]=stk[1].top();
for(int i=s[x];i;i=b[i].n){
int v=b[i].e;
if(v==fa)continue;
f[0][v]=x;
dep[v]=dep[x]+1;
ss0(v,x);
dfnend[x]=dfnend[v];
}
stk[w[x]].pop();
}
void init_lca(){
for(int i=0;i<=m+1;i++)stk[i].push(0);
dep[1]=1;
ss0(1,0);
for(int i=1;i<LN;i++)
for(int j=1;j<=n;j++){
f[i][j]=f[i-1][f[i-1][j]];
}
for(int i=1;i<LM;i++)
for(int j=1;j<=n;j++){
g[i][j]=g[i-1][g[i-1][j]];
h[i][j]=h[i-1][h[i-1][j]];
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=LN-1;i>=0;--i)if(dep[f[i][x]]>=dep[y])x=f[i][x];
if(x==y)return x;
for(int i=LN-1;i>=0;--i)if(f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
return f[0][x];
}
struct query{
int u,v,l,col;
int id;
};
int ans[Q];
vector<query>q;
int tag[N<<2];
inline void pushdown(int x){
if(~tag[x]){
tag[x<<1]=tag[x<<1|1]=tag[x];
tag[x]=-1;
}
}
void mdf(int l,int r,int v,int x,int nl,int nr){
if(nr<l||nl>r)return;
if(l<=nl&&nr<=r){
tag[x]=v;
return;
}
pushdown(x);
int mid=nl+nr>>1;
mdf(l,r,v,x<<1,nl,mid);
mdf(l,r,v,x<<1|1,mid+1,nr);
}
int qry(int p,int x,int nl,int nr){
if(nl==nr)return tag[x];
pushdown(x);
int mid=nl+nr>>1;
if(p<=mid)return qry(p,x<<1,nl,mid);
else return qry(p,x<<1|1,mid+1,nr);
}
int get_col(int x,int d){
x=top1[x];
if(dep[x]<d)return 0;
int ans=1;
for(int i=LM-1;i>=0;--i)
if(dep[g[i][x]]>=d)x=g[i][x],ans+=1<<i;
return ans;
}
int get_cor(int x,int d){
x=qry(dfn[x],1,1,n);
if(dep[x]<d)return 0;
int ans=1;
for(int i=LM-1;i>=0;--i)
if(dep[h[i][x]]>=d)x=h[i][x],ans+=1<<i;
return ans;
}
void solve(const vector<query> &q,int l,int r){
if(l==r-1){
for(int i=0;i<q.size();i++)ans[q[i].id]=l;
return;
}
if(q.empty())return;
int mid=l+r>>1;
mdf(1,n,0,1,1,n);
// cerr<<"mid "<<mid<<endl;
forin(i,nod[mid]) mdf(dfn[*i],dfnend[*i],*i,1,1,n);
// cerr<<"c "<<mid<<" ";for(int i=1;i<=n;i++)cerr<<". "<<qry(dfn[i],1,1,n);cerr<<endl;
vector<query>ql,qr;
for(int i=0;i<q.size();i++){
int cor=get_cor(q[i].v,dep[q[i].l]+1);
// if(q[i].id==3)cerr<<"t "<<mid<<" "<<q[i].v<<" "<<qry(dfn[q[i].v],1,1,n)<<endl;
// cerr<<"cor "<<q[i].v<<" ["<<mid<<"] "<<dep[q[i].l]+1<<" "<<cor<<endl;
// if(q[i].id==3)cerr<<"> "<<q[i].u<<"~"<<q[i].l<<"~"<<q[i].v<<" "<<q[i].col<<" "<<cor<<" "<<mid<<endl;
if(q[i].col+cor>=mid)qr.push_back(q[i]);
else ql.push_back(q[i]);
}
solve(ql,l,mid);
solve(qr,mid,r);
}
int cmp_dep(int x,int y){ return dep[x]<dep[y]; }
int ww[N];
int main(){
freopen("gem3.in","r",stdin);
// freopen("a.in","r",stdin);
freopen("gem.out","w",stdout);
n=getint(),m=getint();
int C=getint(); for(int i=1;i<=m;i++)c[i]=C+1;
for(int i=1;i<=C;i++)c[getint()]=i;
for(int i=1;i<=n;i++)w[i]=c[ww[i]=getint()],nod[w[i]].push_back(i);
m=C+1;
for(int i=1;i<n;i++){
int u=getint(),v=getint();
add(u,v);
add(v,u);
}
init_lca();
for(int i=1;i<=m;i++)sort(nod[i].begin(),nod[i].end(),cmp_dep);
// int qaq=10;while(qaq)cerr<<w[qaq]<<" ",qaq=f[0][qaq];cerr<<endl;
// qaq=10;while(qaq)cerr<<ww[qaq]<<" ",qaq=f[0][qaq];cerr<<endl;
// qaq=916;while(qaq)cerr<<w[qaq]<<"("<<qaq<<") ",qaq=f[0][qaq];cerr<<endl;
// qaq=916;while(qaq)cerr<<ww[qaq]<<" ",qaq=f[0][qaq];cerr<<endl;
int q=getint();
for(int i=0;i<q;i++){
query qi;
int u=qi.u=getint(),v=qi.v=getint();
qi.id=i;
qi.l=lca(u,v);
qi.col=get_col(u,dep[qi.l]);
// cerr<<"> "<<u<<" "<<qi.l<<" "<<v<<" "<<qi.col<<endl;
::q.push_back(qi);
}
solve(::q,0,m);
for(int i=0;i<q;i++)printf("%d\n",ans[i]);
}
滚榜
题意
\(n\) 个队伍,封榜前每个队伍各自通过了 \(a_i\) 道题,封榜期间队伍各过了 \(b_i\) 道题,\(\sum b_i=m\)。滚榜时,主办方按照 \(b_i\) 不降的顺序依次公布各队的 \(b_i\),每公布一队,这队就到达榜首(优先按照 \(a_i+b_i\) 排序,其次按照队伍编号排序)。给定 \(n,a_i,m\),求有多少种可能的最后排行榜。\(n\leq 13,m\leq 500\)。
题解
朴素的 DP 是 \(f[S,\ i,\ j,\ k]\) 表示已经公布了 \(S\) 中的队伍,最近公布的是 \(i\),\(b_i=j\),已经公布的队伍的 \(b\) 之和为 \(k\)。
注意到 \(b_i\) 不降,因此我们可以转而确定 \(\Delta b_i\),我们不再需要记 \(j\) 这一维。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=13,M=510;
int n,m;
int a[N],c[N+1][N+1];
ll f[1<<N][N][M];
int lb[1<<N],popc[1<<N];
int main(){
// freopen("ranklist.in","r",stdin);
// freopen("ranklist.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)scanf("%d",a+i);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
c[i][j]=max(0,a[i]-a[j]+(j>i)),
c[n][j]=max(c[n][j],c[i][j]);
for(int i=0;i<n;i++)
for(int j=1<<i;j<1<<n;j+=2<<i)
lb[j]=i;
for(int i=1;i<1<<n;i++){
popc[i]=popc[i^(i&-i)]+1;
if(i==(i&-i)){
if(c[n][lb[i]]*n<=m)f[i][lb[i]][c[n][lb[i]]*n]=1;
continue;
}
for(int j=0;j<n;j++){
if(!((i>>j)&1))continue;
for(int k=0;k<=m;k++){
int t=i^(1<<j);
for(int l=0;l<n;l++){
if(!((t>>l)&1))continue;
int d=(n-popc[t])*c[l][j];
if(k>=d)f[i][j][k]+=f[t][l][k-d];
}
}
}
}
ll ans=0,S=(1<<n)-1;
for(int i=0;i<n;i++)for(int j=0;j<=m;j++)ans+=f[S][i][j];
cout<<ans;
}
支配
题意
给定一有向图,\(q\) 次独立的询问:如果添加边 \(x\to y\),有多少个点的受支配集会改变。\(n\leq 3000\),\(m\leq 2n\),\(q\leq 2\times 10^4\)
题解
先 \(O(n(n+m))\) 预处理每个点的支配集,接着对于每个点 \(u\),求出 \(1\) 到每个点、每个点到 \(v\) 最少经过多少 \(u\) 的受支配集中的点。每次询问枚举每个 \(u\),判断 \(u\) 的支配集是否减小:\(1\to x\) 的最短路加 \(y\to u\) 的最短路是否小于支配集大小。复杂度 \(O(n(n+m+q))\)
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-')f=-1;
c=getchar();
}
while(isdigit(c)){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
#define it vector<int>::iterator
#define forin(i,v) for(it i=v.begin();i!=v.end();++i)
const int N=3040;
vector<int> to[N],ot[N];
vector<int> d[N]; int ds[N];
bool vis[N],b[N];
int from1[N][N],toi[N][N];
int from1_T[N][N],toi_T[N][N];
int n,m;
void bfs(int st,vector<int> *to){
memset(vis,0,sizeof(bool)*(n+2));
if(b[st])return;
queue<int>q;
q.push(st);vis[st]=1;
while(q.size()){
int t=q.front();q.pop();
forin(i,to[t]){
int v=*i;
if(vis[v])continue;
if(b[v])continue;
q.push(v); vis[v]=1;
}
}
}
void bfs2(int st,vector<int> *to,int *dis){
memset(dis,0x3f,sizeof(int)*(n+2));
dis[st]=b[st];
deque<int>q;
q.push_back(st);
while(q.size()){
int t=q.front();q.pop_front();
forin(i,to[t]){
int v=*i;
if(dis[v]>dis[t]+b[v]){
dis[v]=dis[t]+b[v];
if(b[v])q.push_back(v);
else q.push_front(v);
}
}
}
}
int main(){
freopen("dominator.in","r",stdin);
freopen("dominator.out","w",stdout);
// freopen("C.in","r",stdin);
// freopen("C.my.out","w",stdout);
n=getint(),m=getint();
int q=getint();
for(int i=0;i<m;i++){
int u=getint(),v=getint();
to[u].push_back(v);
ot[v].push_back(u);
}
for(int ban=1;ban<=n;ban++){
b[ban]=1;
bfs(1,to);
b[ban]=0;
for(int i=1;i<=n;i++)if(!vis[i])d[i].push_back(ban);
}
for(int i=1;i<=n;i++){
ds[i]=d[i].size();
forin(j,d[i])b[*j]=1;
bfs2(1,to,from1[i]);
bfs2(i,ot,toi[i]);
forin(j,d[i])b[*j]=0;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
from1_T[i][j]=from1[j][i],
toi_T[i][j]=toi[j][i];
while(q--){
int x=getint(),y=getint();
int ans=0;
int *f=from1_T[x],*t=toi_T[y];
for(int i=2;i<=n;i++){
ans+=(f[i]+t[i]<ds[i]);
}
printf("%d\n",ans);
}
}