天天看點

【GDKOI】2021提高Day2【GDKOI】2021提高Day2第一題 遊戲第二題 群島第三題 抄寫第四題 堆代碼T1T2

【GDKOI】2021提高Day2

提高組 第二試

2021 年 1 月 30 日

注意事項

  1. 嚴格按照題目所要求的格式進行輸入、輸出,否則嚴重影響得分。
  2. 題目測試資料有嚴格的時間限制,逾時不得分。
  3. 輸入檔案格式不用判錯;輸入輸出檔案名均已給定,不用鍵盤輸入。
  4. 送出檔案夾及儲存方式請參考《考生注意事項》。評測以源程式為準。
  5. 評測環境為 NOI 系列活動标準競賽環境,編譯器版本為 g++ 4.8.4。
  6. 對于 C++ 選手,64 位整數輸入輸出格式為 %lld。
  7. 本次競賽的最終解釋權歸 GDOI 評委會所有。

    試題名稱 遊戲 群島 抄寫 堆

    送出檔案名 game.cpp island.cpp copy.cpp heap.cpp

    輸入檔案名 game.in island.in copy.in heap.in

    輸出檔案名 game.out island.out copy.out heap.out

    時間限制 1s 1s 1s 2s

    空間限制 512MB 512MB 512MB 512MB

    編譯選項 -O2

    滿分 100 100 100 100

    第 1 頁 共 7 頁

    GDKOI 2021 TG Day2

第一題 遊戲

送出檔案: game.cpp

輸入檔案: game.in

輸出檔案: game.out

時間空間限制: 1s, 512MB

現在時間是晚上 23:00,您終于把所有的 DDL 給解決了。

于是您打算玩一下遊戲來愉悅身心。

您發現天梯快結算了,于是您打算快速沖到低保。

您現在有 0 顆星,獲得 n 顆星即為低保。每局遊戲勝利可以獲得一顆星,失敗則扣除一顆星。特别地,

當您持有 0 顆星時不再進行扣除。

為了友善,我們簡化掉對局的過程,當您持有 i 顆星的時候,您獲勝的機率為 pi。

您想知道,要沖到低保,期望需要進行多少次對局。出于特殊的考慮,您隻需要知道答案對 998244353 取

模之後的結果

輸入格式

第一行一個整數 n,表示低保所需要的的星數。

接下來 n 行,每行兩個整數 xi

, yi (i = 0, 1, · · · , n n 1),表示機率 pi = xi yi 。

輸出格式

輸出一行一個整數,表示答案對 998244353 取模之後的結果。

換句話說,若答案可以表示為有理數 pq,則你需要輸出 d ∈ [0, 998244353),滿足 dq ≡ p (mod 998244353)。

樣例資料

game.in game.out

2

1 2

1 2

6

資料範圍

對于 30% 的資料,n ≤ 100

對于另 30% 的資料,xi = 1, yi = 2

對于所有資料,1 ≤ n ≤ 106, 1 ≤ xi ≤ yi < 998244353

第 2 頁 共 7 頁

GDKOI 2021 TG Day2

第二題 群島

送出檔案: island.cpp

輸入檔案: island.in

輸出檔案: island.out

時間空間限制: 1s, 512MB

您來到了一處不知名的群島,島嶼從 1 到 n 編号。

您可以通過單向傳送門來在島嶼間穿行。出于當地的習俗,島嶼 i 上隻有通往島嶼 i + 1 和島嶼 ai 的兩

扇單向傳送門。

剛開始時,您在編号為 x 的島上,而您希望前往編号為 1 的島去完成任務,不過您發現這些傳送門并不

一定能把您傳送到 1 号島上,為了友善行動,您希望前往能到達的編号最小的島。

由于時空亂流的影響,島嶼之間的傳送門可能會發生變化。

您需要盡快解決這個問題。

輸入格式

第一行兩個整數 n, m,表示島嶼和事件的數量。

第二行 n 個整數,表示 a1, · · · , an。

接下來 m 行,每行第一個整數 type 表示事件的類型,如果 type = 1,接下來兩個整數 x, y,表示 ax 變

化為 y;如果 type = 2,接下來一個整數 x,表示詢問的起點。

輸出格式

對于每個詢問,輸出一行表示答案。

樣例資料

island.in island.out

4 3

4 1 4 3

2 4

1 3 2

2 4

31

資料範圍

對于所有資料,滿足 n, m ≤ 105, 1 ≤ ai ≤ n。

子任務 1(共 20 分):n, m ≤ 100;

子任務 2(共 20 分):type = 2;

子任務 3(共 60 分):無特殊限制。

第 3 頁 共 7 頁

GDKOI 2021 TG Day2

第三題 抄寫

送出檔案: copy.cpp

輸入檔案: copy.in

輸出檔案: copy.out

時間空間限制: 1s, 512MB

現在你需要抄寫一段話,這段話是由若幹個對稱字元(不超過 26 種對稱字元,為友善表示,使用 aa z

表示這些字元)構成的,每抄寫一個字元 ch,你就需要花費 costch 的時間。

作為一名學生,你當然掌握了一種投機取巧的方法——你可以對折紙張,使得之前抄寫的墨水複制到另

外一半,進而避免機械重複的工作。

形式化地說,假設現在的抄寫進度為 s1s2 · · · sm,則可以做下面的兩種操作:

• 操作一:在 sm 正後面對折紙張,使得抄寫進度變成 s1s2 · · · smsmsmm 1 · · · sk,1 ≤ k ≤ m; • 操作二:在 sm 正中間對折紙張,使得抄寫進度變成 s1s2 · · · smsmm 1 · · · sk,1 ≤ k < m。

而每次對折紙張的時間為 C,那麼最快隻需要多少時間你就能抄完一段話呢?

輸入格式

第一行兩個整數 n, C,表示需要抄寫的字元串的長度,以及對折需要的時間。

第二行 26 個整數,第 i 個整數 costi 表示抄寫第 i 個字元所需要的時間。

第三行一個長度為 n 的字元串 s,保證隻由小寫字母構成。

輸出格式

輸出一個整數,表示抄寫需要的最小時間。

樣例資料

copy.in copy.out

20 2

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

10 10 6 5 8 5 5 5 3

aaabedcecdebccadecbc

67

樣例解釋

初始 s=””

  1. 抄寫 a,花費 9,s=”a”;
  2. 選擇操作一,花費 2,s=”aa”;
  3. 選擇操作二,花費 2,s=”aaa”;
  4. 抄寫 b,花費 1,s=”aaab”;
  5. 抄寫 e,花費 2,s=”aaabe”;
  6. 抄寫 d,花費 5,s=”aaabed”; 第 4 頁 共 7 頁

    GDKOI 2021 TG Day2

  7. 抄寫 c,花費 7,s=”aaabedc”;
  8. 抄寫 e,花費 2,s=”aaabedce”;
  9. 選擇操作二,花費 2,s=”aaabedcecdeb”;
  10. 抄寫 c,花費 7,s=”aaabedcecdebc”;
  11. 選擇操作一,花費 2,s=”aaabedcecdebcc”;
  12. 抄寫 a,花費 9,s=”aaabedcecdebcca”;
  13. 抄寫 d,花費 5,s=”aaabedcecdebccad”;
  14. 抄寫 e,花費 2,s=”aaabedcecdebccade”;
  15. 抄寫 c,花費 7,s=”aaabedcecdebccadec”;
  16. 抄寫 b,花費 1,s=”aaabedcecdebccadecb”;
  17. 選擇操作二,花費 2,s=”aaabedcecdebccadecbc”;

    總代價為 67。

    資料範圍

    data id n = costi

    , C ≤ 性質

    1 10 109 無 2 1000 103 保證 ∀i ≤ j, si = sj 能推出 ∀i ≤ k ≤ j, sk = si 3 1000 105 無 4 1000 109 無 5 105 103 保證 ∀i ≤ j, si = sj 能推出 ∀i ≤ k ≤ j, sk = si 6 105 105 無 7 105 109 無 8 106 103 保證 ∀i ≤ j, si = sj 能推出 ∀i ≤ k ≤ j, sk = si 9 106 105 無

    10 106 109 無 第 5 頁 共 7 頁

    GDKOI 2021 TG Day2

第四題 堆

送出檔案: heap.cpp

輸入檔案: heap.in

輸出檔案: heap.out

時間空間限制: 2s, 512MB

衆所周知,堆是一種可以高效取出最小值的資料結構,其關鍵操作有上濾和下濾。

具體來說,堆是二叉樹的結構,線性的存儲在數組 Heap[1…n] 中,節點 i(1 ≤ i ≤ n) 的權值為 Heap[i],

左兒子為 2i,右兒子為 2i + 1,滿足 Heap[i] ≤ min{Heap[2i], Heap[2i + 1]}。

對節點 x(1 ≤ x ≤ n) 執行上濾操作即不斷比較節點 x 以及其父親的權值,如果更小則交換,C++ 代碼

如下:

1 void Up(int x):

2 while(x>1 && Heap[x]<Heap[x>>1]){

3 swap(Heap[x],Heap[x>>1]);

4 x>>=1

5 }

對于節點 x(1 ≤ x ≤ n) 執行下濾操作 C++ 代碼如下:

1 void Down(int x):

2 while(x2<=n){

3 int y=x2;

4 if (y<n && Heap[y+1]<Heap[y]) y++;

5 if (Heap[y]>=Heap[x])break; 6 swap(Heap[x],Heap[y]);

7 x=y;

8 }

一種時間複雜度是 O(n) 的建構堆的算法是:對于一個 n 個元素的數組 h[1…n],按照從 n 到 1 的順序對

于每個節點執行下濾操作,即可得到一個 n 個節點的堆。

現在有一個 n 的排列 a,以及 q 次詢問,詢問有兩種:

第一種詢問給出區間 [l, r](1 ≤ l ≤ r ≤ n) 以及一個數字 x(1 ≤ x ≤ rr l + 1),對于子序列 a[l…r] 應用上

述 O(n) 建立堆的算法之後,求節點 x 的權值。具體來說,對于 1 ≤ i ≤ rr l + 1,記 b[i] = a[l + ii 1],将 b

作為權值建立一個 rr l + 1 個節點的堆,問堆中節點 x 的權值是多少。

第二種詢問同樣給出區間 [l, r](1 ≤ l ≤ r ≤ n) 以及一個數字 x(1 ≤ x ≤ n),保證 x 是 a[l…r] 中的一

個,同樣對于子序列 a[l…r] 應用上述算法後,求權值為 x 的節點編号。具體來說,對于 1 ≤ i ≤ rr l + 1,記

b[i] = a[l + ii 1],将 b 作為權值建立一個 rr l + 1 個節點的堆,問堆中權值為 x 的節點編号是多少。

輸入格式

第一行兩個整數 n 和 q。

第二行 n 個整數 a[1], · · · , a[n],表示一個 n 的排列。

接下來 q 行,每行四個整數 ty, l, r, x,其中 ty 表示詢問的種類,l, r 表示選擇的區間,x 為對應的節點

編号或者權值。

第 6 頁 共 7 頁

GDKOI 2021 TG Day2

輸出格式

輸出共 q 行,每行輸出對應詢問的答案。

樣例資料

heap.in heap.out

7 10

5 3 2 1 4 6 7

1 5 7 3

1 3 7 5

1 4 5 2

2 5 7 6

2 6 7 7

1 1 5 2

2 3 3 2

2 7 7 7

2 1 6 1

2 3 7 2

7742231112

資料範圍

n, q ≤ 1 × 105

保證 a 是 1, · · · , n 的排列。

Subtask1(30pts):n, q ≤ 1000

Subtask2(20pts):隻有詢問 1;

Subtask3(20pts):隻有詢問 2;

Subtask4(20pts):n, q ≤ 5 × 104;

Subtask5(10pts):沒有特殊限制。

第 7 頁 共 7 頁

代碼

T1

#include<iostream>
#include<cstdio>
using namespace std;
long long mods=998244353;
long long p[1000010],f[1000010],fadd[1000010];
long long ksm(long long x,long long c)
{
	long long ans=1;
	for(x%=mods;c;c>>=1)
	{
		if(c&1) ans=(ans*x)%mods;
		x=(x*x)%mods;
	}
	return ans;
}
int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	long long n,i,tz,tm;
	scanf("%lld",&n);
	for(i=0;i<n;i++)
	{
		scanf("%lld%lld",&tz,&tm);
		p[i]=tz*ksm(tm,mods-2)%mods;
	}
	f[n]=0,fadd[n]=0;
	for(i=n-1;i>0;i--)
	{
		f[i]=((1-p[i])%mods+mods)%mods*ksm(((1-(p[i]*f[i+1])%mods)%mods+mods)%mods,mods-2)%mods;
		fadd[i]=((p[i]*fadd[i+1]%mods)*ksm(((1-p[i]*f[i+1]%mods)%mods+mods)%mods,mods-2)%mods+ksm(((1-p[i]*f[i+1]%mods)%mods+mods)%mods,mods-2))%mods;
	}
	f[0]=(ksm(p[0],mods-2)+fadd[1])%mods*ksm(((1-f[1])%mods+mods)%mods,mods-2)%mods;
	printf("%lld",f[0]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
           

T2

#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
int a[100010],w[100010];
bool q[100010]; 
void change(int x,int L,int R,int l,int r,int s)
{
	if(L==l&&r==R)
	{
		q[x]=s;
		if(q[x])w[x]=R-L+1;
		else if(L==R)w[x]=0;
		else w[x]=w[x<<1]+w[(x<<1)+1];
		return;
	}
	int mid=(L+R)>>1;
//	cout<<x<<' '<<mid<<' '<<L<<' '<<R<<' '<<l<<' '<<r<<' '<<s<<endl;
	if(r<=mid)change(x<<1,L,mid,l,r,s);
	else if(l>mid)change((x<<1)+1,mid+1,R,l,r,s);
	else change(x<<1,L,mid,l,mid,s),change((x<<1)+1,mid+1,R,mid+1,r,s);
	if(q[x])w[x]=R-L+1;
	else w[x]=w[x<<1]+w[(x<<1)+1];
	return;
}
int ask(int x,int L,int R,int l,int r)
{
	if(q[x])return r-l+1;
	if(L==l&&R==r)return w[x];
	int mid=(L+R)>>1;
	if(r<=mid)return ask(x<<1,L,mid,l,r);
	if(l>mid)return ask((x<<1)+1,mid+1,R,l,r);
	return ask(x<<1,L,mid,l,mid)+ask((x<<1)+1,mid+1,R,mid+1,r);
}
int main()
{
	freopen("island.in","r",stdin);
	freopen("island.out","w",stdout);
	int n,m,i,j,l,r,x,y,type,mid;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]<i)change(1,1,n,a[i]+1,i,1);
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d",&type);
		if(type==1)
		{
			scanf("%d%d",&x,&y);
			if(a[x]<x)change(1,1,n,a[x]+1,x,0);
			a[x]=y;
			if(a[x]<x)change(1,1,n,a[x]+1,x,1);
		}
		else
		{
			scanf("%d",&x);
			for(l=1,r=x;l<=r;)
			{
				mid=(l+r)>>1;
				if(ask(1,1,n,mid,x)<x-mid+1)l=mid+1;
				else r=mid-1;
			}
			printf("%d\n",r);
		}
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
           

繼續閱讀