暑期集训day23考试整理

T1: 猫和狗

题目大意:\(k\)个观众,每个观众喜欢一条猫或狗,讨厌一条猫或狗(前猫后狗或前狗后猫),问最多满足多少个观众

1.暴力(\(25pts\)

考场分数:\(5pts\)
原因:

暴力枚举每个状态,一条猫或狗只有选和不选两种状态,\(O(2^n)\)枚举就完事

代码:

#include
#include
#include
#include
#define int long long
using namespace std;
const int maxn=500+5,INF=0x3f3f3f3f;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int n,m,K,hate[maxn],like[maxn],black[maxn],ans,dclock,vis[maxn];
signed main(){

	n=read(),m=read(),K=read();
	for(int i=1;i<=K;i++){
		char ch1,ch3;
		int ch2,ch4;
		cin>>ch1>>ch2>>ch3>>ch4;
		if(ch1=='C'){
			if(!vis[ch2])vis[ch2]=++dclock;
			if(!vis[ch4+n])vis[ch4+n]=++dclock;
			like[i]=vis[ch2],hate[i]=vis[ch4+n];
		}
		if(ch1=='D'){
			if(!vis[ch2+n])vis[ch2+n]=++dclock;
			if(!vis[ch4])vis[ch4]=++dclock;
			like[i]=vis[ch2+n],hate[i]=vis[ch4];
		}
	}
	long long maxs=(1<

2.二分图(\(100pts\)

万万想不到,\(T1\)竟然是二分图原题(然而最后放在二分图的练习的最后一道,压根就没做)

由于一个观众,喜欢猫就讨厌狗,喜欢狗就讨厌猫

所以可以逆向思维,用二分图把冲突的情况处理掉

把喜欢这个动物的与讨厌这个动物的相连,跑二分图,最后答案为:\(n-tot/2\)(有\(tot\)个冲突,说明只能满足一半)

#include
#include
#include
#include
#include
using namespace std;
const int maxn=500+5,INF=0x3f3f3f3f;
inline int read(){
	int s=0,w=1;咕咕
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int n,m,K,ans,a[maxn][maxn],girls[maxn],vis[maxn];
char s1[maxn][maxn],s2[maxn][maxn];
vector g[maxn];
bool Find(int u){
	for(int i=0;i>s1[i]>>s2[i];
	}
	for(int i=1;i<=K;i++){
		for(int j=1;j<=K;j++){
			if(!strcmp(s1[i],s2[j]))g[i].push_back(j),g[j].push_back(i);
		t}
	}
	for(int i=1;i<=K;i++){
		memset(vis,0,sizeof(vis));
		if(Find(i))ans++;
	}
	cout<

T2:旋转子段

题目大意:翻转某个子段,使得翻转后整个序列中满足\(a[i]==i\)的个数最多

1.\(O(n^3)\) 枚举(\(35pts\)

三维,第一维枚举区间长度,第二维枚举左右端点,第三维扫区间,柿子:\(ans=max(sum[l-1]+sum[n]-sum[r]+num)\)\(num\)为区间内满足条件个数)

#include
#include
#include
#include
using namespace std;
const int maxn=5000+5,INF=0x3f3f3f3f;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int sum[maxn],n,f[maxn],a[maxn],ans;
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		sum[i]=sum[i-1];
		if(a[i]==i)sum[i]++;	
	}
	ans=sum[n];
	if(sum[n]==n)return cout<

2.\(O(n^2)\)枚举(\(65pts\)

这里已经用了一个特殊性质了:\(l=min(a[i],i)\) \(r=max(a[i],i)\)

因此三维枚举可以直接转化为:一维枚举中点,第二维左右扩展

#include
#include
#include
#include
using namespace std;
const int maxn=5e5+50;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int n,a[maxn],sum[maxn],ans,l,r,cnt;
int main(){
	n=read();
	for(register int i=1;i<=n;i++){
		a[i]=read();
		sum[i]=sum[i-1];
		if(a[i]==i)sum[i]++;
	}
	for(register int i=1;i<=n;i++){
		x=a[i];
		l=min(x,i),r=max(i,x);
		if(l==r)continue;
		cnt=0;
		while(l<=r){
			if(l!=r){
				if(a[l]==r)cnt++;
				if(a[r]==l)cnt++;
			}else if(l==r){
				if(a[l]==l)cnt++;
			}
			l++,r--;
		}
		ans=max(ans,cnt+sum[min(x,i)]+sum[n]-sum[max(x,i)]);
	}
	cout<

3.桶+\(STL\)\(100pts\)

再细细观察,又能得到第二个性质:如果存在\(a[i]+i==a[j]+j\),那么以\(\frac{i+j}{2}\)为中点,且翻转区间包含\(i\)\(j\),翻转后\(i\)\(j\)都能满足条件

证明略了,干瞪眼观察得到

因此,可以开个桶维护一下\(a[i]+i\),然后枚举每一个\(a[i]+i\),由之前的性质可得到左端点和右端点,然后像前两个一样搞它就完事了

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1e6+5,INF=0x3f3f3f3f;
int n,sum[maxn],a[maxn],ans;
vector ve[maxn];
bool cmp(int x,int y){return abs(2*x-a[x]-x)>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum[i]=sum[i-1];
		if(a[i]==i)sum[i]++;
		ve[a[i]+i].push_back(i);
	}
	for(int i=2;i<=2*n;i++){
		if(!ve[i].empty()){
			sort(ve[i].begin(),ve[i].end(),cmp);
			for(int j=0;jr)swap(l,r);
				ans=max(ans,sum[l-1]+sum[n]-sum[r]+j+1);
			}
		}
	}
	cout<

3.走格子

暑期集训day23考试整理_第1张图片

题目大意:一个矩阵,一堆能走的点,期间能在墙上开传送门,然后求从起点到终点的最短路线

感觉是考试中较简单的一道题了,然而打的正解挂了\(75pts\),原因是没注意必须要在墙上开门,以为可以原地开门(其实就是没考虑全,毕竟很多时候要开传送门的格子旁边就是墙)

乱搞就完事了,一个格子对上下左右能走的地方连边

然后考虑传送门,由于传送门只能由墙边传到墙边,所以可以直接从当前格子往上下左右走,看最后停的位置,然后连边,边权为上下左右中最短的距离\(+1\),因为必须要走到墙边才能打传送门,如图

暑期集训day23考试整理_第2张图片

代码:

#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000+5,maxe=6e6+5,INF=0x3f3f3f3f;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int n,m,ans=INF,tx,vis[maxe],ty,qx,qy,head[maxe],tot,dis[maxe],inq[maxe];
char a[maxn][maxn];
struct Edge{
	int to,next,from,dis;
}e[maxe];
void Add(int x,int y,int z){
	e[++tot].next=head[x];
	e[tot].to=y;
	e[tot].from=x;
	e[tot].dis=z;
	head[x]=tot;
}
void Spfa(int S){
	for(int i=0;i<=n*m*2;i++)dis[i]=INF;
	dis[S]=0;
	queue q;q.push(S);
	while(!q.empty()){
		int u=q.front();q.pop();vis[u]=0;//cout<dis[u]+e[x].dis){
				dis[v]=dis[u]+e[x].dis;
				if(!vis[v]){
				//	if(++inq[v]>n*m)break;
					q.push(v);vis[v]=1;
				}
			}
		}
	}
	if(dis[(tx-1)*m+ty]==INF)puts("no");
	else cout<>a[i][j];
			if(a[i][j]=='C')qx=i,qy=j,a[i][j]='.';
			if(a[i][j]=='F')tx=i,ty=j,a[i][j]='.';
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]=='.'){
				if(a[i+1][j]=='.')Add((i-1)*m+j,i*m+j,1);
				if(a[i-1][j]=='.')Add((i-1)*m+j,(i-2)*m+j,1);
				if(a[i][j+1]=='.')Add((i-1)*m+j,(i-1)*m+j+1,1);
				if(a[i][j-1]=='.')Add((i-1)*m+j,(i-1)*m+j-1,1);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]=='.'){
				int x1=i,x2=i,y1=j,y2=j,diss=INF;
				while(a[x1+1][j]=='.')x1++;
				while(a[i][y1+1]=='.')y1++;
				while(a[x2-1][j]=='.')x2--;
				while(a[i][y2-1]=='.')y2--;
				diss=min(min(x1-i+1,i-x2+1),min(y1-j+1,j-y2+1));
				Add((i-1)*m+j,(x1-1)*m+j,diss);
				Add((i-1)*m+j,(x2-1)*m+j,diss);
				Add((i-1)*m+j,(i-1)*m+y1,diss);
				Add((i-1)*m+j,(i-1)*m+y2,diss);
			}
		}
	}
	Spfa((qx-1)*m+qy);
	return 0;
}

T4:柱状图

模拟退火,\(O(1)\)随机搞最大高度,\(O(n)\)枚举最高点坐标

口古口古口古

你可能感兴趣的:(暑期集训day23考试整理)