noip模拟41[太险了吧!!!]

noip模拟41 solutions

这次考得挺迷的,然后就直接上100了,我本来只有14pts的

T1 你相信引力嘛

我是真的很迷,我在本机小样例拍一个错一个,交上去拿了90pts

然后我就先特判了一下cao过去了,好像挺不要脸的

后来发现是我将相同高度的点缩到一起的时候有一点点的小问题

这个题就是一个单调栈维护序列的题

先把序列复制一边,模拟环状,然后就开始扫

前一半可以弹栈也可以加栈,后一半只能弹栈,然后最后减去最大值造成的重复贡献就好了

相同高度的缩一下,保证线性的时间复杂度

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=5e6+5;
ll n,a[N*2];
ll st[N*2],sta[N*2],fro,beh,sum[N*2];
ll ans;
ll mx,ms,rec;
signed main(){
	scanf("%lld",&n);fro=1;beh=0;
	for(re i=1;i<=n;i++)scanf("%lld",&a[i]),a[n+i]=a[i],mx=max(mx,a[i]);
	for(re i=1;i<=n;i++)if(a[i]==mx)ms++;
	rec=1;if(ms>2)rec=ms*(ms-1)/2ll;
	for(re i=1;i<=n;i++){
		while(fro<=beh&&a[sta[beh]]<a[i])ans+=sum[beh],beh--;
		for(re j=beh;j>=fro;j--){
			if(a[sta[j]]!=a[i]){ans++;break;}
			ans+=sum[j];
		}
		if(fro<=beh&&a[sta[beh]]==a[i])sum[beh]++;
		else sta[++beh]=i,sum[beh]=1,st[beh]=i;
	}
	ll maxn=0;
	for(re i=n+1;i<=2*n;i++){
		while(fro<=beh&&sta[fro]+n<=i){
			sta[fro]++;sum[fro]--;
			while(a[sta[fro]]!=a[st[fro]]&&sum[fro])sta[fro]++;
			if(!sum[fro])fro++;
		}
		if(a[i]<maxn)continue;
		while(fro<=beh&&a[sta[beh]]<a[i])ans+=sum[beh],beh--;
		for(re j=beh;j>=fro;j--){
			if(a[sta[j]]!=a[i]){ans++;break;}
			ans+=sum[j];
		}
		maxn=a[i];
	}
	printf("%lld",ans-rec);
}

T2 marshland

考场上我看到这个题之后,一眼就是状压啊,50位。。好!!!

我直接上long long然后00000000000000pts

后来考完了之后,好像有点像网络流啊

直接将有危险的点放在中间,拆成入点和出点,中间连边费用为危险度

然后将x为奇数的点放在左边和s连边,x为偶数的放在右边和t连边

然后把相邻的都连上,所有边的流量都是1,除了上面的费用其他费用都是0

直接跑最大费用可行流,这个就是在跑最大流的过程中记录最大值就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=55;
const int M=N*N*2;
int n,m,k,ans,sum;
int inner[N][N],outer[N][N],usier[N][N],tot=3;
int jz[N][N],s=1,t=2;
bool limt[N][N],vis[M];
int to[M*4],nxt[M*4],val[M*4],cst[M*4],head[M],rp=1;
void add_edg(int x,int y,int z,int c){
	to[++rp]=y;
	val[rp]=z;
	cst[rp]=c;
	nxt[rp]=head[x];
	head[x]=rp;
}
int pre[M],dis[M],whe[M];
queue<int> q;
bool spfa(){
	memset(dis,-0x3f,sizeof(dis));//cout<<dis[1]<<endl;
	//memset(vis,false,sizeof(vis));
	//memset(pre,0,sizeof(pre));
	//memset(whe,0,sizeof(whe));
	vis[s]=true;q.push(s);dis[s]=0;
	while(!q.empty()){
		int x=q.front();vis[x]=false;q.pop();
		//cout<<x<<endl;
		for(re i=head[x];i;i=nxt[i]){
			int y=to[i];
			//if(y==0)cout<<x<<" "<<y<<endl;
			if(!val[i])continue;
			if(dis[x]+cst[i]<=dis[y])continue;
			dis[y]=dis[x]+cst[i];pre[y]=x;whe[y]=i;
			if(!vis[y])q.push(y),vis[y]=true;
		}
	}
	return dis[t]>0;
}
signed main(){
	scanf("%d%d%d",&n,&m,&k);
	for(re i=1;i<=n;i++)
		for(re j=1;j<=n;j++)
			scanf("%d",&jz[i][j]),sum+=jz[i][j];
	for(re i=1,x,y;i<=k;i++)scanf("%d%d",&x,&y),limt[x][y]=true;
	for(re i=1;i<=n;i++)
		for(re j=1;j<=n;j++){
			if(limt[i][j])continue;
			if(!jz[i][j]){
				usier[i][j]=++tot;
				if(i&1) add_edg(s,usier[i][j],1,0),add_edg(usier[i][j],s,0,0);
				else add_edg(usier[i][j],t,1,0),add_edg(t,usier[i][j],0,0);
				continue;
			}
			inner[i][j]=++tot;outer[i][j]=++tot;
			add_edg(inner[i][j],outer[i][j],1,jz[i][j]);
			add_edg(outer[i][j],inner[i][j],0,-jz[i][j]);
		}
	for(re i=1;i<=n;i++){
		for(re j=1;j<=n;j++){
			if(!jz[i][j])continue;
			if(i-1>0&&!limt[i-1][j])
				if((i-1)&1) add_edg(usier[i-1][j],inner[i][j],1,0),add_edg(inner[i][j],usier[i-1][j],0,0);
				else add_edg(outer[i][j],usier[i-1][j],1,0),add_edg(usier[i-1][j],outer[i][j],0,0);
			if(j-1>0&&!limt[i][j-1])
				if(i&1) add_edg(usier[i][j-1],inner[i][j],1,0),add_edg(inner[i][j],usier[i][j-1],0,0);
				else add_edg(outer[i][j],usier[i][j-1],1,0),add_edg(usier[i][j-1],outer[i][j],0,0);
			if(i+1<=n&&!limt[i+1][j])
				if((i+1)&1) add_edg(usier[i+1][j],inner[i][j],1,0),add_edg(inner[i][j],usier[i+1][j],0,0);
				else add_edg(outer[i][j],usier[i+1][j],1,0),add_edg(usier[i+1][j],outer[i][j],0,0);
			if(j+1<=n&&!limt[i][j+1])
				if(i&1) add_edg(usier[i][j+1],inner[i][j],1,0),add_edg(inner[i][j],usier[i][j+1],0,0);
				else add_edg(outer[i][j],usier[i][j+1],1,0),add_edg(usier[i][j+1],outer[i][j],0,0);
		}
	}
	ans=sum;int now=0;
	while(now<m&&spfa()){
		now++;//cout<<now<<endl;
		sum-=dis[t];ans=min(ans,sum);
		for(re i=t;i!=s;i=pre[i])val[whe[i]]--,val[whe[i]^1]++;
	}
	printf("%d",ans);
}

T3 party?

其实这个也可以用网络流解决,直接在人和特产之间连边

人和s连,特产和t连,然后二分去卡人和s的连边的流量就好了

但是这样做只会有71pts,因为懒所以没有实现

那么我们有一个定理叫做Hall定理

大概是这样的,如果一个二分图存在完美匹配(就是可以用一些端点不重合的边覆盖二分图的所有点)

第一个条件,二分图两个集合大小相等
第二个条件,保证我当前点集能够连到的对面点集的大小大于当前集合,这个对当前点集的所有子集成立

证明我也不会,爱咋咋地

hall定理是维护二分图完美匹配,也就是一个点对应一个点

而这个题就是一个点对应多个点,但是这些点仍然是没有交集的

所以我们可以把每个人的特产看作一个点,这样就成功的转化成了二分图问题

然后我们就直接用bitset维护每个人能够拿到的特产,然后直接枚举人的所有点集

直接找到特产数然后除以人数,一直这样取最小值,最后就是答案

AC_code


#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=3e5+5;
const int M=1005;
int n,m,q,v[N];
int to[N],nxt[N],head[N],rp;
void add_edg(int x,int y){
	to[++rp]=y;
	nxt[rp]=head[x];
	head[x]=rp;
}
int fa[N],siz[N],son[N],dep[N];
void dfs_fi(int x){
	siz[x]=1;son[x]=0;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		dep[y]=dep[x]+1;
		dfs_fi(y);siz[x]+=siz[y];
		if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
	}
}
int top[N],dfn[N],idf[N],cnt;
void dfs_se(int x,int f){
	top[x]=f;dfn[x]=++cnt;idf[cnt]=x;
	if(son[x])dfs_se(son[x],f);
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==son[x])continue;
		dfs_se(y,y);
	}
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
bitset<M> fro[N],now[6];
void dfs_fo(int x){
	if(top[x]!=x)fro[x]=fro[fa[x]];
	fro[x].set(v[x]);
	for(re i=head[x];i;i=nxt[i])dfs_fo(to[i]);
}
int qus[15],ans;
struct XDS{
	#define ls x<<1
	#define rs x<<1|1
	bitset<M> tr[N*4];
	void pushup(int x){
		tr[x]=tr[ls]|tr[rs];
		return ;
	}
	void build(int x,int l,int r){
		if(l==r){tr[x].set(v[idf[l]]);return ;}
		int mid=l+r>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		pushup(x);return ;
	}
	bitset<M> query(int x,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr)return tr[x];
		int mid=l+r>>1;
		bitset<M> ret;ret.reset();
		if(ql<=mid)ret=ret|query(ls,l,mid,ql,qr);
		if(qr>mid)ret=ret|query(rs,mid+1,r,ql,qr);
		return ret;
	}
	#undef ls
	#undef rs
}xds;
bitset<M> query(int x,int y){
	bitset<M> ret;ret.reset();
	while(top[x]!=top[y]){
		ret=ret|fro[x];
		x=fa[top[x]];
	}
	ret=ret|xds.query(1,1,n,dfn[y],dfn[x]);
	return ret;
}
signed main(){
	scanf("%d%d%d",&n,&m,&q);
	for(re i=2;i<=n;i++)scanf("%d",&fa[i]),add_edg(fa[i],i);
	for(re i=1;i<=n;i++)scanf("%d",&v[i]);
	dfs_fi(1);dfs_se(1,1);dfs_fo(1);xds.build(1,1,n);
	cout<<endl<<endl;
	for(re i=1;i<=q;i++){
		//cout<<"sb"<<" "<<i<<endl;
		scanf("%d",&qus[0]);
		for(re j=1;j<=qus[0];j++)scanf("%d",&qus[j]);
		int lca=LCA(qus[1],qus[2]);
		for(re j=3;j<=qus[0];j++)lca=LCA(lca,qus[j]);
		for(re j=1;j<=qus[0];j++)now[j]=query(qus[j],lca);//cout<<now[j].count()<<" ";
		//cout<<"sb"<<endl;
		//cout<<lca<<endl;
		bitset<M> no;ans=0x3f3f3f3f;
		for(re j=1;j<(1<<qus[0]);j++){
			int tmp=0;no.reset();
			for(re k=1;k<=qus[0];k++)
				if((j>>k-1)&1)no=no|now[k],tmp++;
			//for(re k=1;k<=m;k++)cout<<no[k]<<" ";cout<<endl;
			//int an=no.count();an/=tmp;cout<<no.count()<<" "<<tmp<<endl;
			ans=min(ans,(int)no.count()/tmp);
		}
		printf("%d\n",ans*qus[0]);
	}
}

T4 半夜

这个题好像是根据一篇论文出的,但是我没有找到那个论文

这个叫\(\mathcal{O(n^2)}\)求最长循环同构子序列

我也没看太懂那个官方题解,我在这里口胡一下吧

你发现我由前面向后面转移的时候,后面的一定>=前面的,这个好像显然吧

所以我们就有了一个分界点,我去nmd分界点。。。。。

就是在转移过程中,会出现一个点,让我们的转移+1,而这个点是可以递推出来的

我们把第一个序列复制一边

设f[i][j][k]表示复制之后i-j位上,和第二个串前k位匹配的最大长度

那么我的i越靠后f[i][j-1][k]的值会越小,接近匹配上的程度越大,所以这里存在一个分界点让大的可以+1,小的不行

如果f[i][j][k-1]的话,你发现,对k来说,我增加了一个字符,那么对于当前字符来说

我最好让前面都匹配上,所以i肯定是越小越好

这个就直接按着转移就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=2005;
int n,m;
char a[N<<1],b[N];
int p[N<<1][N],q[N<<1][N];
signed main(){
	scanf("%d%s%s",&n,a+1,b+1);
	memcpy(a+n+1,a+1,n);m=n*2;
	for(re j=1;j<=m;j++)p[j][0]=j;
	for(re j=1;j<=m;j++){
		for(re k=1;k<=n;k++){
			int pp=p[j][k-1],qq=q[j-1][k];
			if(a[j]!=b[k]&&pp>qq)p[j][k]=pp,q[j][k]=qq;
			else p[j][k]=qq,q[j][k]=pp;
		}
	}
	int ans=0;
	for(re i=1;i<=n;i++){
		int tmp=0;
		for(re j=i;j<n+i;j++)tmp+=(i>p[j][n]);
		ans=max(ans,tmp);
	}
	printf("%d",ans);
}

最后吧,我觉得这个转移的含义主要是要无论从那一个转移过来

最后都让他相等

posted @ 2021-08-20 16:03  fengwu2005  阅读(68)  评论(0编辑  收藏  举报