【GDKOI】2021 普及 Day1

【GDKOI】2021 普及 Day1

第一题 地图

提交文件: map.cpp
输入文件: map.in
输出文件: map.out
时间空间限制: 1s, 512MB
Alice 得到了一张神秘地图,并将神秘地图的规则给了你。
0 c2 c3 · · · cn 1 a1,n
r2 a2,2 a2,3 · · · a2,n 1 a2,n
r3 a3,2 a3,3 · · · a3,n 1 a3,n
… … … … … … rn 1 an 1,2 an 1,3 · · · an 1,n 1 an 1,n
an,1 an,2 an,3 · · · an,n 1 an,n
其中 a 的值为 0 或 1,ri
, ci 定义如下:
ci = a2,i ⊕ a3,i ⊕ a4,i ⊕ · · · ⊕ an,i ⊕ a1,n ⊕ a2,n ⊕ · · · ⊕ an,n
ri = ai,2 ⊕ ai,3 ⊕ ai,4 ⊕ · · · ⊕ ai,n ⊕ an,1 ⊕ an,2 ⊕ · · · ⊕ an,n
即 ci 表示第 i 列和第 n 列里面全部 a 的异或和,ri 表示的是第 i 行和第 n 行全部 a 的异或和。
很不幸的是,现在这个地图里面有一个数字错了,你能找出来吗?

输入格式

第一行一个整数 n,表示矩阵大小。
接下来 n 行,每行 n 个整数,表示一个 n × n 的矩阵。
输出格式
一行,两个整数 x, y,表示 (x, y) 位置出错。

样例数据

map.in

4
0 0 0 0
0 0 1 1
1 1 1 0
1 1 1 0

map.out

2 2

样例解释

把 (2, 2) 改正之后,矩阵为
0 0 0 0
0 1 1 1
1 1 1 0
1 1 1 0
GDKOI 2021 PJ Day1

数据范围

对于 30% 的数据,n ≤ 4
对于 60% 的数据,n ≤ 200
对于 100% 的数据,4 ≤ n ≤ 2000

思路

8种区域,造成的r,c变化不同。
根据r,c,错误数量和位置

代码

#include<iostream>
#include<cstdio>
using namespace std;
int a[2010][2010],c[2010],r[2010];
int main()
{
    
    
	int n,i,j,cn,rn,sumr,sumc,rw,cw;
	freopen("map.in","r",stdin);
	freopen("map.out","w",stdout);
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	for(cn=rn=0,i=1;i<=n;i++)
		rn^=a[n][i],cn^=a[i][n];
	for(sumr=sumc=0,i=2;i<n;i++)
	{
    
    
		for(c[i]=cn,r[i]=rn,j=2;j<=n;j++)
			r[i]^=a[i][j],c[i]^=a[j][i];
		if(r[i]!=a[i][1]) sumr++,rw=i;
		if(c[i]!=a[1][i]) sumc++,cw=i;
	}
	if(sumr==n-2&&sumc==n-2) printf("%d %d\n",n,n);
	else if(sumr==n-2&&sumc==0) printf("%d 1\n",n);
	else if(sumr==0&&sumc==n-2) printf("1 %d\n",n);
	else if(sumr==1&&sumc==n-2) printf("%d %d\n",rw,n);
	else if(sumr==n-2&&sumc==1) printf("%d %d\n",n,cw);
	else if(sumr==1&&sumc==0) printf("%d 1\n",rw);
	else if(sumr==0&&sumc==1) printf("1 %d\n",cw);
	else printf("%d %d\n",rw,cw);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

第二题 灌水

提交文件: water.cpp
输入文件: water.in
输出文件: water.out
时间空间限制: 1s, 512MB
小 A 是一个 OI 界的灌水大师。现在他有一个宽度为 n 的水槽,水槽的底部并不是水平的,位置 i 处的
高度为 ai,并且 a0 = an+1 = + ∞。如下图,黑色格子表示水槽底部,蓝色和红色格子表示水。(该图也
是样例数据的示意图)
在这里插入图片描述

图 1: 水槽
要使得某处的水位增加 1,需要 1 单位的水。水位服从“水往低处流”的物理规律,具体地说,我们在
将 ax 的水位提升至 y 时,对于任意位置 i,如果 i 与 x 之间所有位置(包括 i)的高度都小于 y,那么 i 处
的水位也要提升至 y。
现在小 A 想进行 q 次操作,每个操作有两个参数 x, y,表示小 A 想要通过在位置 x 处灌水使得位置 x
处的水位最终达到 y,其中 y > ax。对于每一次操作,你需要告诉小 A 他需要多少单位水。操作之间相互
独立。

输入格式

第一行两个整数 n, q,表示水槽的宽度以及操作的个数。
第二行 n 个整数,第 i 个整数表示位置 i 处的高度 ai。
接下来 q 行,每行两个整数,表示一个操作的参数 x, y。

输出格式

共 q 行,每行一个整数,分别表示每一个操作需要多少水。

样例数据

water.in

8 5
1 3 2 7 3 2 9 8
5 6
4 8
1 6
3 3
7 10

water.out

7
30
12
1
45

GDKOI 2021 PJ Day1

样例解释

参考图 1,红色代表第一个询问的灌水情况,蓝色代表第三个询问的灌水情况。

数据范围

对于 40% 的数据,n, q ≤ 5000;
对于 100% 的数据,n ≤ 2 × 105, 0 < ai ≤ 109, 1 ≤ x ≤ n, ax < y ≤ 109

思路

用单调队列和二分查找优化。

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
long long a[200010],sum[200010],lleft,rleft,lright,rright;
struct jgt
{
    
    
	long long x,s;
}queleft[200010],queright[200010];
struct qt
{
    
    
	long long x,s,id,ans,r;
}question[200010];
long long read()
{
    
    
	char c;
	long long ans;
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(ans=c-'0',c=getchar();c>='0'&&c<='9';ans=(ans<<3)+(ans<<1)+c-'0',c=getchar());
	return ans;
}
void write(long long x)
{
    
    
	if(x==0) return;
	write(x/10),putchar(char(x%10+'0'));
	return;
}
void print(long long x)
{
    
    
	if(x==0)putchar('0');
	else write(x);
	putchar('\n');
	return;
}
bool cmp(qt t1,qt t2){
    
    return t1.x<t2.x;}
bool cmp_(qt t1,qt t2){
    
    return t1.id<t2.id;}
long long leftfind(long long s)
{
    
    
	long long l,r,mid;
	for(l=lleft,r=rleft;l<r;)
	{
    
    
		mid=(l+r+1)>>1;
		if(s<=queleft[mid].s) l=mid;
		else r=mid-1;
	}
	return queleft[r].x;
}
long long rightfind(long long s)
{
    
    
	long long l,r,mid;
	for(l=lright,r=rright;l<r;)
	{
    
    
		mid=(l+r+1)>>1;
		if(s<=queright[mid].s) l=mid;
		else r=mid-1;
	}
	return queright[r].x;
}
int main()
{
    
    
	long long n,q,i,j,tem;
	freopen("water.in","r",stdin);
	freopen("water.out","w",stdout);
	n=read(),q=read();
	lleft=lright=1,rleft=rright=0;
	queleft[++rleft].s=queright[++rright].s=10000000000;
	queleft[rleft].x=0,queright[rright].x=n+1;
	for(sum[0]=0,i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+a[i];
	for(i=1;i<=q;i++) question[i].x=read(),question[i].s=read(),question[i].id=i;
	sort(question+1,question+1+q,cmp);
	for(i=j=1;i<=q;i++)
	{
    
    
		for(;j<=question[i].x;j++)
		{
    
    
			for(;a[j]>=queleft[rleft].s&&lleft<=rleft;rleft--);
			queleft[++rleft].s=a[j],queleft[rleft].x=j;
		}
		question[i].ans=leftfind(question[i].s);
	}
	for(i=q,j=n;i>0;i--)
	{
    
    
		for(;j>=question[i].x;j--)
		{
    
    
			for(;a[j]>=queright[rright].s&&lright<=rright;rright--);
			queright[++rright].s=a[j],queright[rright].x=j;
		}
		tem=rightfind(question[i].s);
		question[i].ans=question[i].s*(tem-question[i].ans-1)-sum[tem-1]+sum[question[i].ans];
	}
	sort(question+1,question+1+q,cmp_);
	for(i=1;i<=q;i++) print(question[i].ans);//printf("%d\n",question[i].ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

第三题 配对

提交文件: match.cpp
输入文件: match.in
输出文件: match.out
时间空间限制: 1s, 512MB
Alice 在玩游戏。
游戏规则为:一开始系统会给你一个区间 [l, r],然后生成 n 个数(n 为偶数)。Alice 的任务是将这 n 个
数两两配对,使得每一对的和都属于 [l, r]。
现在 Alice 在一关被卡住了,于是他找来了 Bob。
聪明的 Bob 发现这一关可能是不存在一个合法方案的,幸运的是 Bob 手里有修改器。通过修改器,他
可以删掉某些数。为了防止作弊被抓,Bob 想通过尽量少的修改(也可以不删),使得 Alice 可以过关。
如果剩下奇数个数是无法过关的,但是如果全删完了 Alice 即可立刻过关。

输入格式

第一行一个整数,表示 n, l, r,保证 n 为偶数。
第二行 n 个整数,表示序列 a1, a2, · · · , an。

输出格式

输出一个整数,表示至少在里面删掉多少个数,可以使得 Alice 可以过关。

样例数据

match.in

4 9 33
31 23 13 10

match.out

2

match.in

10 18 71
49 30 41 26 32 41 71 49 70 38

match.out

4

样例解释

对于样例 1, 我们可以删去 31, 23,剩下 10, 13,可以发现 9 ≤ 10 + 13 ≤ 33。而我们并不能在一个数不删的情况下找到一个配对方案。

数据范围

对于 20% 的数据,n ≤ 10;
对于 30% 的数据,n ≤ 20;
对于 50% 的数据,n ≤ 100;
对于 70% 的数据,n ≤ 1000;
对于 100% 的数据,n ≤ 106, 0 ≤ ai ≤ 109

思路

用贪心,排序,两端比较,把不符合的一端删除。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int a[1000010];
int main()
{
    
    
	int i,n,l,r,left,right,ans=0;
	freopen("match.in","r",stdin);
	freopen("match.out","w",stdout);
	scanf("%d%d%d",&n,&l,&r);
	for(i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	for(left=1,right=n;left<right;)
	{
    
    
		if(a[left]+a[right]<l) left++,ans++;
		else if(a[left]+a[right]>r) right--,ans++;
		else left++,right--;
	}
	printf("%d",(ans+1)/2*2);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

第四题 旅行

提交文件: travel.cpp
输入文件: travel.in
输出文件: travel.out
时间空间限制: 1s, 512MB
小 A 打算去 X 市进行旅行。
X 市有 n 个旅游景点,有 m 条在景点间往返的观光专线。每条观光专线连接两个的旅游景点,乘坐某
条观光专线需要花费 w 元。注意可能存在不同的观光专线连接相同的景点,以及可能一条观光专线起点与终点相同。
而 X 市推出了优惠政策,即如果小 A 提前买了 p 元的旅行通票,那么就可以免费乘坐价格小于等于 p
元的观光专线。
现在小 A 有 q 个旅行计划,每个计划从景点 x 开始旅行,并且花费不超过 w 元,他想知道最多能去多
少个景点。每个旅行计划之间相互独立。

输入格式

第一行包含两个整数 n, m,表示 X 市的景点数与观光专线的数量。
接下来 m 行,每行三个整数 u, v, w,表示一条观光专线连接的两个节点与价格。
接下来一行,一个整数 q,表示旅行计划数。
接下来 q 行,每行两个整数 x, w,表示旅行计划的起点与花费。

输出格式

共 q 行,每行一个整数,表示最多可以去多少个景点。

样例数据

travel.in

3 3
1 2 5
1 3 4
2 3 6
2
1 9
2 4

travel.out

3
1

数据范围

对于 20% 的数据,n, m, q ≤ 5000;
对于 50% 的数据,n, q ≤ 5000, m ≤ 4 × 105
对于 100% 的数据,n, q ≤ 2 × 105, m ≤ 4 × 105, 0 < w ≤ 109

思路

离线后排序,按钱从小到大,用并查集连接,保存结果,按输入顺序排序。

代码

#include<iostream>
#include<cstdio>
#include<algorithm> 
using namespace std;
struct jgt
{
    
    
	int x,y,w;
}a[400010];
struct question
{
    
    
	int x,w,id,ans;
}b[200010];
int f[200010],s[200010];
bool cmpb(jgt t1,jgt t2){
    
    return t1.w<t2.w;}
bool cmpquestion(question t1,question t2){
    
    return t1.w<t2.w;}
bool cmpans(question t1,question t2){
    
    return t1.id<t2.id;}
int find(int x)
{
    
    
	if(f[x]==x) return x;
	return f[x]=find(f[x]);
}
int main()
{
    
    
	int n,m,q,i,j,dx,dy,tot=0;
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
	{
    
    
		tot++;
		scanf("%d%d%d",&a[tot].x,&a[tot].y,&a[tot].w);
		tot-=(a[tot].x==a[tot].y);
	}
	sort(a+1,a+1+tot,cmpb);
	for(scanf("%d",&q),i=1;i<=q;i++) scanf("%d%d",&b[i].x,&b[i].w),b[i].id=i;
	sort(b+1,b+q+1,cmpquestion);
	for(i=1;i<=n;f[i]=i,s[i]=1,i++);
	for(i=j=1;i<=q;i++)
	{
    
    
		for(;j<=m&&a[j].w<=b[i].w;j++)
		{
    
    
			dx=find(a[j].x),dy=find(a[j].y);
			if(dx!=dy) f[dy]=dx,s[dx]+=s[dy],s[dy]=0;
		}
		b[i].ans=s[find(b[i].x)];
	}
	sort(b+1,b+1+q,cmpans);
	for(i=1;i<=q;i++) printf("%d\n",b[i].ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_46975572/article/details/113276790
今日推荐