noip模拟41

A. 你相信引力吗

很明显的单调栈的题,考场上没有想到平移最大值,而是想着复制一倍序列破环成链,然后发现最大值的部分特别难维护,而且耗费时间过长,只好牺牲时间复杂度加了个 \(map\) 去重。

首先把一个最大值放到最左边,这样除了最大值都不能跨过左端点走另一半的环进行匹配
然后维护单调不增栈,对于多个连续值维护当前是第几个,往后递推即可
过程中需要特判最大值防止算重

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7+5;
int n,a[maxn],mxpos,sta[maxn],f[maxn],tp;
long long ans;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();	
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();	
	}
	return x*f;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i+n]=a[i]=read();
		if(a[i]>a[mxpos])mxpos=i;
	}
	for(int i=mxpos;i<mxpos+n;i++){
		while(tp&&a[sta[tp]]<a[i])ans++,tp--;
		if(a[sta[tp]]>a[i])ans++;
		else ans+=f[sta[tp]]+(a[i]!=a[mxpos]);
		sta[++tp]=i;
		f[i]=(a[i]==a[sta[tp-1]])?f[sta[tp-1]]+1:1;
	}
	while(tp>2){
		if(a[sta[tp]]==a[sta[2]])break;
		tp--;
		ans++;	
	}
	cout<<ans;
	return 0;
}

B. marshland

考虑网络流:

如果把格子黑白染色,那么一个有危险值的格子流量为 \(2\),但是费用流并不能控制其恰好为 \(2\),可能会出现流量为 \(1\) 但费用算入贡献的情况

那么可以按如下方法染色:

pic.png

将棋盘染成类似上述三种颜色:

那么一种合法的方案一定由蓝到红再到黄(假定红色是危险的格子)

那么把红色格子拆点放在中间,连一条费用为危险值的边,蓝色作为左边点,原点向其连边,然后连向中间入点;黄色点放右边,从出点向其连边,然后连向汇点

注意这道题要求可行流,比起传统费用流来讲并不是有流就流,而是当前路径费用为正才流


C. party?

特产的选择方案比较奇怪,可以用网络流维护

这其实是一个二分图,左部点是 \(c\) 个人,每个人相当于拥有 \(k\) 的流量,然后每个人向其对应特产连边,特产流量为一
现在要计算满足满流的最大流量
根据 \(Hall\) 定理,二分图存在完美匹配当且仅当对于任意的 \(k\),左部任意 \(k\) 个点在右边的相邻点个数大于等于 \(k\)
由于 \(c\) 很小,直接枚举所有情况即可

对于统计一条路径上特产有哪些,可以开一个 \(bitset\),并用树剖维护
为了防止跳链浪费时间,树剖时对于完整的重链记录其所达特产,只在最后一部分线段树上查询

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
const int len=1005;
const int inf=0x3f3f3f3f;
int n,m,id[maxn],fa[maxn],x,dep[maxn],siz[maxn],son[maxn],re[maxn],tp[maxn],tot,num,pos[maxn],q,hd[maxn],cnt,c[maxn];
bitset<len>col[maxn],S[10];
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
struct Edge{
	int nxt,to;
}edge[maxn];
void add(int u,int v){
	edge[++cnt].nxt=hd[u];
	edge[cnt].to=v;
	hd[u]=cnt;
	return ;
}
void dfs(int u){
	siz[u]=1;
	for(int i=hd[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		dep[v]=dep[u]+1;
		dfs(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
	return ;
}
void dfs1(int u,int top){
	tp[u]=top;
	id[u]=++tot;
	re[tot]=u;
	if(u!=top)col[u]=col[fa[u]];
	col[u].set(c[u]);//(1<<c[u]);
//	cout<<u<<"    "<<col[u]<<endl;
	if(son[u])dfs1(son[u],top);
	for(int i=hd[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==son[u])continue;
		dfs1(v,v);
	}
	return ;
}
int lca(int x,int y){
	while(tp[x]!=tp[y]){
		if(dep[tp[x]]<dep[tp[y]])swap(x,y);
		x=fa[tp[x]];	
	}
	if(dep[x]<dep[y])swap(x,y);
	return y;
}
struct Seg{
	int l,r;
	bitset<len>sc;
}t[maxn*4];
void update(int p){
	t[p].sc|=t[p<<1].sc|t[p<<1|1].sc;
	return ;
}
void build(int p,int l,int r){
	t[p].l=l;
	t[p].r=r;
	if(l==r){
		t[p].sc.set(c[re[l]]);
//		t[p].sc|=(1<<c[re[l]]);
		return ;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	update(p);
	return ;
}
bitset<len>ask(int p,int l,int r){
	if(t[p].l>=l&&t[p].r<=r){
		return t[p].sc;
	}
	int mid=t[p].l+t[p].r>>1;
	bitset<len>ans;
	if(l<=mid)ans=ask(p<<1,l,r);
	if(r>mid)ans|=ask(p<<1|1,l,r);
	return ans;
}
bitset<len>que(int x,int ed){
	bitset<len>ans;
	while(tp[x]!=tp[ed]){
		ans|=col[x];
		x=fa[tp[x]];
	}
	ans|=ask(1,id[ed],id[x]);
	return ans;
}
int main(){
	n=read();
	m=read();
	q=read();
	for(int i=2;i<=n;i++){
		fa[i]=read();
		add(fa[i],i);
	}
	for(int i=1;i<=n;i++)c[i]=read();
	dfs(1);
	dfs1(1,1);
//	for(int i=1;i<=n;i++)cout<<col[i]<<endl;
	build(1,1,n);
	for(int i=1;i<=q;i++){
		num=read();
		int d;
		for(int j=1;j<=num;j++){
			if(j==1)d=pos[j]=read();
			else d=lca(d,pos[j]=read());
		}
		for(int j=1;j<=num;j++){
			S[j]=que(pos[j],d);
//			cout<<S[j]<<endl;
		}
		tot=inf;
		for(int T=1;T<=(1<<num)-1;T++){
			bitset<len>sum;
			int pnum=0;
			for(int j=1;j<=num;j++){
				if(T&(1<<(j-1))){
					pnum++;
					sum|=S[j];
				}
			}
			int x=sum.count();
			tot=min(tot,x/pnum);
		}
		for(int j=1;j<=num;j++)S[j].reset();
		printf("%d\n",tot*num);
	}
	return 0;
}

D. 半夜

首先题意转化为将 \(A\) 串复制一倍,没一个子串与 \(B\) 串的 \(LCS\)
暴力 \(n^3\)
复习一下 \(LCS\) 转移方程式为 \(f[i][j]=min(f[i-1][j],f[i][j-1],f[i-1][j-1]+1)\)

考虑优化:
\(f_{i,j,k}=LCS(S[i,j],T[1,k])\)
很明显有 \(f_{i-1,j,k}>f_{i-1,j-1,k}\) 可以推出 \(f_{i,j,k}>f_{i,j-1,k}\)
同理 \(f_{i,j,k}>f_{i,j,k-1}\) 推出 \(f_{i-1,j,k}>f_{i-1,j,k-1}\)

那么发现 \(dp\) 的转移有分解点 \(p_{j,k}\)\(q_{j,k}\) 表示:

\[f_{i,j,k}=f_{i,j−1,k}+[i≥p_{k,j}]=f_{i,j,k−1}+[i<q_{k,j}] \]

那么推出下图(盗图 \(from\) 这篇博客):

这是 \(P<Q\)\(S[j]!=T[k]\) 的情况,设 \(f_{i-1,j-1,k-1}=F\) 根据递推式推出前两行,取 \(max\) 推出第三行,发现 \(p_{j,k}=P\)\(q_{j,k}=Q\)
所有 \(S[j]=T[k]\) 的情况和这个相同

这是 \(P>Q\) 的情况,同理推出 \(p_{j,k}=Q\)\(q_{j,k}=P\)

这样两个分界点数组可以递推出来,然后实现 \(n^2\) 转移

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=4005;
char a[maxn],b[maxn];
int n,p[maxn][maxn],q[maxn][maxn],sum,ans;
int main(){
	cin>>n;
	scanf("%s",a+1);
	scanf("%s",b+1);
	for(int i=1;i<=n;i++)a[i+n]=a[i];
	for(int i=1;i<=n*2;i++)p[i][0]=i+1,q[i][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n*2;j++){
			int P=p[j][i-1],Q=q[j-1][i];
			if(a[j]!=b[i]&&P>Q){
				p[j][i]=P,q[j][i]=Q;
			}
			else p[j][i]=Q,q[j][i]=P;
		}
	}
	for(int i=1;i<=n;i++){
		sum=0;
		for(int j=i;j<n+i;j++){
			if(i>=p[j][n])sum++;
		}
		ans=max(ans,sum);
	}
	cout<<ans;
	return 0;
}
posted @ 2021-08-16 21:02  y_cx  阅读(68)  评论(2编辑  收藏  举报