1. 程式人生 > 其它 >【GDKOI】2021 普及 Day1

【GDKOI】2021 普及 Day1

技術標籤:GDKOI

【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;
}