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,横跨xi和yi的茎。例如:
第一天长出了一条茎,高度为1,横跨了(1,4)
第二天又长出了一条茎,高度为2,横跨了(3,7)
第三天,高度为3,横跨了(1,6)
第四天,高度为4,横跨了(2,6)
而每天,新长出来的茎,会与之前的茎相交(严格相交,可看图),如果相交则会开花。
现在研究者希望你写个程序来计算一下,每天会有多少朵新的花开放。
输入格式
第一行一个数N,表示植物的生命周期。
接下来N行,每描述一对(xi,yi),表示这一天生长的茎,横跨了xi和yi。
输出格式
N行,每行一个数,表示每天新开放的花的个数。
输入样例
4
1 4
3 7
1 6
2 6
输出样例
0
1
1
2
数据规模
对于60%的数据N≤3000
对于100%的数据 N≤100 000,1≤xi<yi≤100 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。接下来N行N列,表示画布的最终状态。
【输出格式】
一个整数,表示可能的颜色数。
【输入样例】
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%的数据,1≤N≤50
100%的数据 1≤N≤1000
3.双面棋盘(chessboard)
题目描述:
小A有一个N*N的棋盘,每一个格子有黑白两面且可以翻转。小A对棋盘进行了M次操作,每一次翻转了一个格子。小A迫切的想知道棋盘上连通块的个数(本题中两个格子互相连通当且仅当两个格子颜色相同且有公共边),但是小A太小不会做。作为他的哥哥大A,你自然要帮他解决这个问题。
输入格式:
第一行一个数N,表示棋盘大小。
接下来是一个N*N的01矩阵,记录棋盘的初始状态。(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涂抹题解:
开一个新的n∗n的矩阵cnt,初始全0
对于每个数字统计出出现的最上u最下d最左l最右r,然后就能框出一个矩形,把cnt[u,d][l,r]加上1,表示有一个矩形在这个位置至少涂了一次
如果一个格子的cnt>1,说明有超过一个矩形在这里涂了数字,那么这个格子最终的数字一定不能最先涂
剩下的就是能最先涂的数字。
特殊情况:n≠1且最终矩阵里除了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]);
}
}