2021 省选联考 Day 2 意识流题解【倍增 整体二分 状压DP BFS】

宝石

洛谷 P7518

题意

给定一无根树,点有颜色。给定颜色序列 \(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]);
}


滚榜

洛谷 P7519

题意

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

支配

洛谷 P7520

题意

给定一有向图,\(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);
    }
}

posted @ 2021-04-14 19:41  破壁人五号  阅读(92)  评论(0编辑  收藏  举报