2018.12.22模拟赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/85227411

肥肠良心的一场模拟赛了···

T1

【题目描述】
Mirko想在一块土地上种草莓。
这块土地可以看作一个N行M列格子组成的矩形,这些格子中有些适合种植草莓,有些不适合。Mirko希望在矩形中找出一片满足种植要求的长方形区域,即区域中每个格子都适合种植。
这时Mirko突然想到一个问题。设格子(i,j)的权值W(i,j)表示有多少个满足种植要求的长方形区域包含了这个格子。那么所有格子的权值之和是多少?

【输入格式】
第一行为两个正整数N, M。
接下来N行,每行是一个长度为M的字符串,依次表示矩形的每行。字符串只包含 ‘.’ 和 ‘#’ 两种字符,前者表示格子适合种植草莓,后者表示不适合。

【输出格式】
输出一行,一个整数,表示答案。

【样例输入1】
2 3
.#.
…#

【样例输出1】
8

【样例输入2】
2 3

【样例输出2】
40

【样例解释】
样例1中所有格子的权值矩阵如下:
2 0 1
3 2 0

【数据规模与约定】
对于50%的数据,1 ≤ N, M ≤ 300.
对于100%的数据,1 ≤ N, M ≤ 2000.

solution:
可以翻译成找有多少个矩形都是 . . ,答案就是这些矩形的大小加起来

然后找极大子矩形的问题就可以转化成单调栈了?
维护一个单调递增的栈,如果遇到一个比较矮的一边弹栈一边计算答案,但因为要不重不漏,所以要判断一下 t o p 1 , t o p , n o w top-1,top,now 的大小关系,每次切的时候只能切凸出来的那个矩形,然后把剩下的和 t o p 1 top-1 合并

计算的时候可以推一推柿子,就是用等差数列什么的,很好推:
假设矩形宽为 x x ,高可以从 y 1 y1 y 2 y2 ,那么此处的贡献就是
( y 1 + y 2 ) ( y 2 y 1 + 1 ) / 2 ( 1 L L ( x + 1 ) ( x + 1 ) x / 2 1 L L x ( x + 1 ) ( 2 x + 1 ) / 6 ) (y1+y2)*(y2-y1+1)/2*(1LL*(x+1)*(x+1)*x/2-1LL*x*(x+1)*(2*x+1)/6)

考场代码如下:(考试脑抽还以为自己写的 n 3 n^3 ···

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 2005
#define LL long long
using namespace std;

inline int rd(){
	int x=0,f=1;char c=getchar();
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

int n,m,a[maxn][maxn],stk[maxn],top,now,f[maxn];
char s[maxn][maxn];
LL ans;

inline LL calc(int x,int y1,int y2){
	return 1LL*(y1+y2)*(y2-y1+1)/2*(1LL*(x+1)*(x+1)*x/2-1LL*x*(x+1)*(2*x+1)/6);
}

int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
//	freopen("in.txt","r",stdin);
//	freopen("out2.txt","w",stdout);
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	for(int i=1;i<=m;i++){
		int cnt=0;
		for(int j=n;j;j--){
			if(s[j][i]=='.') cnt++;
			else cnt=0;
			a[j][i]=cnt;
		}
	}
	for(int i=1;i<=n;i++){
		top=0; now=1; f[0]=0; a[i][m+1]=0;
		while(now<=m+1){
			int cnt=0;
			while(top && stk[top]>=a[i][now]){
				int x=f[top],y1,y2;
				if(a[i][now]>stk[top-1]) y2=stk[top],y1=a[i][now];
				else y2=stk[top],y1=stk[top-1],f[top-1]+=f[top];
				ans+=calc(x,y1+1,y2);
				top--; cnt=x;
			}
			stk[++top]=a[i][now],f[top]=cnt+1; now++;
		}
	}
	printf("%lld\n",ans);
	return 0;
}

T2

【题目描述】
Kreso最近找了份送外卖的兼职工作。
在A市有N家餐馆,编号为1 ~ N。它们由恰好N-1条路相连,使得任意两家餐馆相互可达。每天,Kreso首先要花费M单位时间从这些餐馆取外卖。
Kreso最初位于1号餐馆。每1单位时间,他要么从所在餐馆取出外卖;要么移动到一个相邻的餐馆。注意,经过一个餐馆并不会取外卖,取外卖要单独花费1单位时间。
设N家餐馆的外卖价值分别是A1到AN。请问在这次取外卖的过程中,Kreso取得的所有外卖的价值之和最大是多少?

【输入格式】
第一行,两个正整数N, M。
第二行,N个用空格隔开的正整数A1, …, AN。
接下来N-1行,每行两个整数U, V,表示有一条路连接U号餐馆和V号餐馆。

【输出格式】
输出一行,一个整数,表示答案。

【样例输入】
3 5
9 2 5
1 2
1 3

【样例输出】
14

【样例解释】
Kreso首先花费1单位时间取出1号餐馆外卖,然后花费1单位时间移动到3号餐馆,然后花费1单位时间取出3号餐馆外卖,剩余2单位时间无用。取得外卖价值之和为9 + 5 = 14。

【数据规模与约定】
对于30%的数据,1 ≤ N, M ≤ 100.
对于100%的数据,1 ≤ N, M ≤ 500, 1 ≤ U, V ≤ N, 1 ≤ Ai ≤ 106.

solution:
这个···一开始还以为有多难···其实就是个树形 d p dp ,因为数据范围感人竟然十分怀疑正确性,不过后来想了想因为题目原因这个数据范围大概是正确的叭

f [ u ] [ i ] [ 0 / 1 ] f[u][i][0/1] 表示 u u 为根的子树,花了 i i 时间,有没有回到根的最大价值,转移的时候注意限制一下没有回到根的只能有一个,方程:
f [ u ] [ j ] [ 0 ] = m a x ( f [ u ] [ j ] [ 0 ] , f [ v ] [ k ] [ 0 ] + f [ u ] [ j k 2 ] [ 0 ] ) f[u][j][0]=max(f[u][j][0],f[v][k][0]+f[u][j-k-2][0])
f [ u ] [ j ] [ 1 ] = m a x ( f [ u ] [ j ] [ 1 ] , f [ v ] [ k ] [ 0 ] + f [ u ] [ j k 2 ] [ 1 ] ) f[u][j][1]=max(f[u][j][1],f[v][k][0]+f[u][j-k-2][1])
f [ u ] [ j ] [ 1 ] = m a x ( f [ u ] [ j ] [ 1 ] , f [ v ] [ k ] [ 1 ] + f [ u ] [ j k 1 ] [ 0 ] ) f[u][j][1]=max(f[u][j][1],f[v][k][1]+f[u][j-k-1][0])

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 505
using namespace std;

inline int rd(){
	int x=0,f=1;char c=getchar();
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}
inline int min(int x,int y){return x<y?x:y;}

int n,m,a[N],cnt,head[N],to[N<<1],nxt[N<<1],f[N][N][2],siz[N],ans;

inline void add(int x,int y){
	to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
	to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt;
}

void DP(int u,int fa){
	siz[u]=1; f[u][1][0]=a[u]; f[u][1][1]=a[u];
	for(int i=head[u],v;i;i=nxt[i])
		if((v=to[i])!=fa){
			DP(v,u); siz[u]+=siz[v];
			for(int j=min(m,3*siz[u]);j;j--)
				for(int k=1;k<min(j,3*siz[v]);k++){
					if(j-k-2>=0) {
						f[u][j][0]=max(f[u][j][0],f[v][k][0]+f[u][j-k-2][0]);
						f[u][j][1]=max(f[u][j][1],f[v][k][0]+f[u][j-k-2][1]);
					}
					f[u][j][1]=max(f[u][j][1],f[v][k][1]+f[u][j-k-1][0]);
				}
		}
}

int main(){
	freopen("food.in","r",stdin);
	freopen("food.out","w",stdout);
//	freopen("in.txt","r",stdin);
//	freopen("out2.txt","w",stdout);
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) a[i]=rd();
	for(int i=1;i<n;i++){
		int x=rd(),y=rd();
		add(x,y);
	}
	DP(1,0);
	for(int i=1;i<=m;i++) ans=max(ans,max(f[1][i][0],f[1][i][1]));
	printf("%d\n",ans);
	return 0;
}

T3

【题目描述】
Zlatko是学校里校车项目的负责人。
整个市区可以看成一个二维平面直角坐标系。有N名学生和M个校车停靠点,坐标均已知。共有K条校车线路,每条线路只会发一辆车,每辆车经过若干个校车停靠点后最终抵达学校。一辆校车至多容纳C名学生。每个校车停靠点可以容纳任意多名学生。
现在问题来了,究竟让每名同学到哪个校车停靠点等车呢?
假设一名学生到一个地点的用时等于两者坐标的欧几里得距离的平方,即(x1 – x2)2 + (y1 – y2)2。Zlatko希望让所有学生都能上车并且所有人中用时最长者的时间最少。请你帮助他算出最佳方案。数据保证至少存在一个可行方案。

【输入格式】
输入文件名为bus.in
第一行,四个正整数N, M, C, K。
接下来N行,每行两个整数ux, uy,表示一名学生的坐标。
接下来M 行,每行两个整数sx, sy,表示一个校车停靠点的坐标。
接下来K行,每行表示一条校车路线:首先是一个整数Ki,表示这条路线的停靠点数目,接下来是Ki个1到M之间的整数,表示经过哪些停靠点。

【输出格式】
输出文件名为bus.out。
共一行,一个整数,表示最佳方案中用时最长学生的用时。

【样例输入】
3 3 2 2
1 3
2 2
8 7
3 4
6 7
8 4
2 1 2
1 3

【样例输出】
9

【样例解释】
最佳方案中,学生1和2前往停靠点1。此时,线路1已满员了。因此,学生3只能前往停靠点3,用时为9。

【数据规模与约定】
对于40%的数据,C = 1.
对于100%的数据,1 ≤ N, M, K, C ≤ 100, -1000 ≤ ux, uy, sx, sy ≤ 1000。

solution:
常规思路二分答案···判断的话,一开始还以为要如何如何贪心或者 d p dp ,然后发现这不是网络流模型嘛

二分完就知道了每个学生可以去哪些站,进而知道每个学生可以坐哪些车,然后源点向每个学生连容量为 1 1 的边,学生向可以坐的车连 1 1 的边,每个车向汇点连 C C 的边,跑一个最大流判断流量是否 = n =n 就好了

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define N 205
#define M 25005
#define inf 0x3f3f3f3f
using namespace std;

inline int rd(){
	int x=0,f=1;char c=getchar();
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

int n,m,C,K,a[N][2],b[N][2],s,t;
vector<int> c[N];
int cnt=1,head[N],nxt[M],to[M],w[M],dis[N],cur[N];
bool canto[N];

inline void add(int x,int y,int z){
	to[++cnt]=y,nxt[cnt]=head[x],w[cnt]=z,head[x]=cnt;
	to[++cnt]=x,nxt[cnt]=head[y],w[cnt]=0,head[y]=cnt;
}

inline bool bfs(){
	queue<int> q; q.push(s);
	memset(dis,0,sizeof dis); dis[s]=1;
	memset(cur,0,sizeof cur); cur[s]=head[s];
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int i=head[u],v;i;i=nxt[i])
			if(w[i] && !dis[v=to[i]]){
				dis[v]=dis[u]+1;
				cur[v]=head[v];
				if(v==t) return true;
				q.push(v);
			}
	} return false;
}

int dfs(int u,int fl){
	if(u==t || !fl) return fl;
	int res=0;
	for(int i=head[u],v;i;i=nxt[i])
		if(w[i] && dis[v=to[i]]==dis[u]+1){
			int tmp=dfs(v,min(fl-res,w[i]));
			w[i]-=tmp,w[i^1]+=tmp,res+=tmp;
			if(fl==res) break;
		} return res;
}

inline int dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(s,inf);
	return ans;
}

inline int sq(int x){return x*x;}

inline bool check(int mx){
	cnt=1; memset(head,0,sizeof head);
	for(int i=1;i<=n;i++){
		add(s,i,1); memset(canto,0,sizeof canto);
		for(int j=1;j<=m;j++){
			if(sq(b[j][0]-a[i][0])+sq(b[j][1]-a[i][1])<=mx)
				for(int k=0;k<c[j].size();k++) canto[c[j][k]]=1;
		}
		for(int j=1;j<=K;j++)
			if(canto[j]) add(i,j+n,1);
	}
	for(int i=1;i<=K;i++) add(i+n,t,C);
	return dinic()==n;
}

int main(){
	freopen("bus.in","r",stdin);
	freopen("bus.out","w",stdout);
//	freopen("in.txt","r",stdin);
//	freopen("out2.txt","w",stdout);
	n=rd(); m=rd(); C=rd(); K=rd(); t=n+K+1;
	for(int i=1;i<=n;i++) a[i][0]=rd(),a[i][1]=rd();
	for(int i=1;i<=m;i++) b[i][0]=rd(),b[i][1]=rd();
	for(int i=1;i<=K;i++){
		int x=rd();
		for(int j=1;j<=x;j++){
			int y=rd();
			c[y].push_back(i);
		}
	}
	int l=0,r=8000000,mid,ans=0;
	while(l<=r){
		mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}

总分 100 + 100 + 100 = 300 100+100+100=300

好多人都 A K AK 了···

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/85227411