游老师18 02 28模拟赛解题报告

4 篇文章 0 订阅
3 篇文章 0 订阅

Noip2017练习赛#8

 

 

中文题目名称

外星植物

涂抹

双面棋盘

英文题目与子目录名

plant

art

chessboard

可执行文件名

plant

art

chessboard

输入文件名

plant.in

art.in

chessboard.in

输出文件名

plant.out

art.out

chessboard.out

 每个测试点时限

1S

1S

1S

内存限制

512MB

512MB

512MB

结果比较方式

全文比较

题目类型

传统

传统

传统

 

 

1.外星植物

题目描述

在遥远的星球,有一种奇怪外星植物。为了方便起见,我们将这个植物放到平面坐标系中来看。

这种植物由N条茎组成,每天长出一条茎,N天后则会凋萎。第i天长出一个高度为i,横跨xiyi的茎。例如:

 

第一天长出了一条茎,高度为1,横跨了(1,4)

 

第二天又长出了一条茎,高度为2,横跨了(3,7)

 

第三天,高度为3,横跨了(1,6)

 

第四天,高度为4,横跨了(2,6)

而每天,新长出来的茎,会与之前的茎相交(严格相交,可看图),如果相交则会开花。

现在研究者希望你写个程序来计算一下,每天会有多少朵新的花开放。

 

输入格式

第一行一个数N,表示植物的生命周期。

接下来N行,每描述一对(xi,yi),表示这一天生长的茎,横跨了xiyi

 

输出格式

N行,每行一个数,表示每天新开放的花的个数。

 

输入样例

4

1 4

3 7

1 6

2 6

 

输出样例

0

1

1

2

 

数据规模

对于60%的数据N3000

对于100%的数据 N100 0001xi<yi100 000

 

 

2.涂抹

【问题描述】

有一个N*N大小的画布,初始时,相当于有N*N个格子,每个格子上有一个数字0。每次给画布涂色时,可以涂1..N2中的某种颜色,每次涂的区域为一个矩形。如给定一个4*4大小的画布,初始时为:

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

当选定每个矩形区域,涂颜色2时,如下所示:

2 2 2 0

2 2 2 0

2 2 2 0

0 0 0 0

当选定每个矩形区域,涂颜色7时,如下所示:

2 2 2 0

2 7 7 7

2 7 7 7

0 0 0 0

当选定每个矩形区域,涂颜色3时,如下所示:

2 2 3 0

2 7 3 7

2 7 7 7

0 0 0 0

每次涂的矩形可以为1*1,也可以为N*N的大小,每种颜色仅使用一次。给定画布的最终状态,求有多少种颜色有可能是最先涂的。

【输入格式】

第一行一个整数N,表示画布大小为N*N。接下来NN列,表示画布的最终状态。

【输出格式】

一个整数,表示可能的颜色数。

【输入样例】

4

2 2 3 0

2 7 3 7

2 7 7 7

0 0 0 0

【输出样例】

14

【样例解释】

由最终状态可以看出,颜色3和颜色7必定在颜色2后面涂的;其他颜色也可能最先涂。总共有16种颜色,减掉颜色3和颜色7,共14种。

【数据范围】

40%的数据,1N50

100%的数据 1N1000

3.双面棋盘chessboard

题目描述:

A有一个N*N的棋盘,每一个格子有黑白两面且可以翻转。小A对棋盘进行了M次操作,每一次翻转了一个格子。小A迫切的想知道棋盘上连通块的个数(本题中两个格子互相连通当且仅当两个格子颜色相同且有公共边),但是小A太小不会做。作为他的哥哥大A,你自然要帮他解决这个问题。

输入格式:

第一行一个数N,表示棋盘大小。

接下来是一个N*N01矩阵,记录棋盘的初始状态。(0:白,1:黑)

接下来一行一个数M,表示操作个数。

接下来M行,每行两个数,表示小A操作的坐标。

输出格式:

M行,每行两个数,表示当前棋盘上黑色连通块与白色连通快的个数。

输入样例:

5

0 1 0 0 0

0 1 1 1 0

1 0 0 0 1

0 0 1 0 0

1 0 0 0 0

2

3 2

2 3

输出样例

4 3

5 2

样例解释:

数据范围:

对于30%数据,N<=10,M<=100;

对于50%数据,N<=100,M<=500;

对于所有数据,N<=500,M<=1500;


T1外星植物题解:因为插入高度i是从小到大的,所以每次只需统计第i根之前有几根穿过xi,yi。这里要注意,上次以开花的地方这次不会开花了,所以要减去这部分花的数目

代码:

#include<bits/stdc++.h>
using namespace std;
int f[1000000],g[1000000];
void pushdown(int x,int l,int r){
	if(f[x]){
		f[x*2]+=f[x];
		f[x*2+1]+=f[x];
		f[x]=0;
	}
}
int cha(int x,int l,int r,int t){
	if(l==r)return f[x];
	pushdown(x,l,r);
	int mid=(l+r)/2;
	if(mid>=t)return cha(x*2,l,mid,t);
	 else return cha(x*2+1,mid+1,r,t);
}
void xiu(int x,int l,int r,int t,int k){
	if(l==t&&r==k){
		f[x]++;
		return;
	}
	int mid=(l+r)/2;
	if(mid>=k)xiu(x*2,l,mid,t,k);
	 else if(mid<t)xiu(x*2+1,mid+1,r,t,k);
	  else{
	  	xiu(x*2,l,mid,t,mid);
	  	xiu(x*2+1,mid+1,r,mid+1,k);
	  }  
}
int main(){
freopen("plant.in","r",stdin);
freopen("plant.out","w",stdout);	
	int n,i,t,k,s2,s1;
	scanf("%d",&n);
	printf("%d\n",n);
	for(i=1;i<=n;i++){
		scanf("%d%d",&t,&k);
		s1=cha(1,1,100000,t);
		s2=cha(1,1,100000,k);
		printf("%d\n",s1+s2-g[t]-g[k]);
		g[t]=s1;g[k]=s2;
		if(t+1<k)xiu(1,1,100000,t+1,k-1);
	} 
}



T2涂抹题解:

开一个新的nn的矩阵cnt,初始全0 
对于每个数字统计出出现的最上u最下d最左l最右r,然后就能框出一个矩形,把cnt[u,d][l,r]加上1,表示有一个矩形在这个位置至少涂了一次 
如果一个格子的cnt>1,说明有超过一个矩形在这里涂了数字,那么这个格子最终的数字一定不能最先涂 
剩下的就是能最先涂的数字。 

特殊情况:n1且最终矩阵里除了0外只有一种数字,那么这个数字肯定不能先涂,要从答案中扣除。

代码:

#include <cstdio>r
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1010
using namespace std;
typedef long long LL;
int n,cnt,ans;

int a[M][M],b[M][M];
int u[M*M],d[M*M],l[M*M],r[M*M];
bool killed[M*M];
inline LL read(){
	char c; LL x = 0;
	c = getchar();
	while (c < '0' || '9' < c) c = getchar();
	while ('0' <= c && c <= '9'){
		x = x*10 + c-'0';
		c = getchar();
	}
	return x;
}
int main()
{
    freopen("art.in","r",stdin);
    freopen("art.out","w",stdout);
	n=read();
    memset(u,0x3f,sizeof u);
    memset(l,0x3f,sizeof l);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            a[i][j]=read();
            if(a[i][j])
            {
                if(u[a[i][j]]==0x3f3f3f3f)
                    ++cnt;
                u[a[i][j]]=min(u[a[i][j]],i);
                d[a[i][j]]=max(d[a[i][j]],i);
                l[a[i][j]]=min(l[a[i][j]],j);
                r[a[i][j]]=max(r[a[i][j]],j);
            }
        }
    for(int i=1;i<=n*n;i++)
        if(u[i]!=0x3f3f3f3f)
        {
            b[u[i]][l[i]]++;
            b[d[i]+1][l[i]]--;
            b[u[i]][r[i]+1]--;
            b[d[i]+1][r[i]+1]++;
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(a[i][j] && b[i][j]>1)
                killed[a[i][j]]=true;
    for(int i=1;i<=n*n;i++)
        if(!killed[i])
            ++ans;
    if(cnt==1 && n!=1)
        --ans;
    cout<<ans<<endl;
    return 0;
}


T3双面棋盘题解:并查集+线段树乱搞,线段树维护l行到r列的联通情况,并查集来判断。看过代码应该就知道了。

代码:

#include<bits/stdc++.h>
using namespace std;
struct aaa{
	int bw[2],u[501],d[501];
}f[5000];
int n,a[1000][1000],g[5000],fa[5000];
int find(int t){
	if(t==fa[t])return t;
	fa[t]=find(fa[t]);
	return fa[t];
}
void xiu1(int x,int mid){
    int i,lc=x*2,rc=x*2+1,t,k;
	for(i=0;i<2;i++)f[x].bw[i]=f[lc].bw[i]+f[rc].bw[i];
	for(i=1;i<=n;i++){
		fa[i]=f[lc].u[i];
		fa[i+n]=f[lc].d[i];
		fa[n*2+i]=f[rc].u[i]+n*2;
		fa[n*3+i]=f[rc].d[i]+n*2;
	}
	for(i=1;i<=n;i++)
	 if(a[mid][i]==a[mid+1][i]){
	 	t=find(i+n);k=find(n*2+i);
	 	if(t!=k){
	 		fa[t]=k;
	 		f[x].bw[a[mid][i]]--;
	 	}
	 }
	 memset(g,0,sizeof(g));
	 for(i=1;i<=n;i++){
	 	f[x].u[i]=find(i);
	 	if(!g[f[x].u[i]])g[f[x].u[i]]=i;
	 	f[x].u[i]=g[f[x].u[i]];
	 	f[x].d[i]=find(n*3+i);
	 	if(!g[f[x].d[i]])g[f[x].d[i]]=i+n;
	 	f[x].d[i]=g[f[x].d[i]];
	 }
}
void build(int x,int l,int r){
	int i;
	if(l==r){
	 f[x].u[1]=f[x].d[1]=f[x].bw[a[l][1]]=1;
	 for(i=2;i<=n;i++){
	 	if(a[l][i]==a[l][i-1])f[x].u[i]=f[x].d[i]=f[x].u[i-1];
	 	 else{
	 	  f[x].u[i]=f[x].d[i]=i;
	 	  f[x].bw[a[l][i]]++;
	 }
	 }
	 return;
}
	int mid=(l+r)/2;
	build(x*2,l,mid);
	build(x*2+1,mid+1,r);
	xiu1(x,mid);
}
void xiu(int x,int l,int r,int t){
	int i;
	if(l==r){
		f[x].u[1]=f[x].d[1]=f[x].bw[a[l][1]]=1;
		f[x].bw[1-a[l][1]]=0;
	 for(i=2;i<=n;i++){
	 	if(a[l][i]==a[l][i-1])f[x].u[i]=f[x].d[i]=f[x].u[i-1];
	 	 else{
	 	  f[x].u[i]=f[x].d[i]=i;
	 	  f[x].bw[a[l][i]]++;
	 }
	 }
	 return;
	}
	int mid=(l+r)/2;
	if(mid>=t)xiu(x*2,l,mid,t);
	 else xiu(x*2+1,mid+1,r,t);
	xiu1(x,mid); 
}
int main(){
freopen("chessboard.in","r",stdin);
freopen("chessboard.out","w",stdout);	
	int i,j,x,y,m;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	 for(j=1;j<=n;j++)scanf("%d",&a[i][j]);
	build(1,1,n); 
	scanf("%d",&m);
	for(i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		a[x][y]=1-a[x][y];
		xiu(1,1,n,x);
		printf("%d %d\n",f[1].bw[1],f[1].bw[0]);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值