CSP模擬1

wlesq發表於2024-07-19

T1「Wdsr-2.5」琪露諾的算數遊戲

題目描述

遊戲概況

《琪露諾的算數遊戲》(諢名“⑨牌”),是一款輕鬆快樂的多玩家卡牌回合制遊戲。

注意:這裡的規則與市面上的⑨牌規則不盡相同。由於⑨牌種類太多不大容易處理,所以這裡的規則更類似於 \(\text{NEU}\) 遊戲。

遊戲中有 \(n\) 名玩家,圍成一圈。一共會進行 \(m\) 輪。每個玩家初始時有 \(3\) 張手牌。遊戲有一個 \(k\) 張牌的牌堆。在本題中,你可以認為不會出現牌堆抽完的情況(真的)。此外,根據該題給出的規則,你不需要考慮選手手牌的順序。

為了簡述遊戲規則,你可以認為每一輪遊戲中有一個整型變數(類似於 \(\text{int}\) 型別暫存器) \(p\) 。玩家打出的牌本質上是對 \(p\) 進行操作。

:請注意下文中“局”、“輪”、“回合”的關係。本題你只會進行一局遊戲,每局有 \(m\) 輪,每一輪會有若干回合,每一回合會有一名玩家出牌。

每一輪開始時,\(p\) 會被初始化為 \(0\) ,然後從初始玩家開始,按照順時針順序\(1,2,3,\cdots n-1,n,1,2,\cdots\) ,逆時針同理),依次出牌。如果這是第一輪,那麼初始玩家就是 \(1\) 號玩家。當某個玩家出完某張牌後,如果此時 \(p> 99\) ,視作該玩家成為該局的失敗者;否則她就會立刻從牌堆頂部取出一張牌並進入到下一回合。失敗者會丟失手上其餘的兩張牌,並從牌堆頂部依次摸三張牌放入自己的手牌中。同時,失敗者會成為下一輪初始玩家。在一局遊戲當中,牌堆裡的牌只減不增。被使用的牌不會回到牌堆當中。

下面介紹該魔改版遊戲的牌型。

基本牌

基本牌可以分為五類:加法牌、減法牌、乘法牌、除法牌、固定牌。

  • 加法牌,一共有 \(7\) 種: \(A_{1},A_{2},A_{5},A_{9},A_{19},A_{49},A_{99}\) 。其中, \(A_x\) 的作用效果是,使 \(p\) 加上牌面上的數字。即 \(p\gets p+x\)
  • 減法牌,一共有 \(3\) 種: \(B_{1},B_{9},B_{19}\) 。作用效果與加法牌類似,只不過會使 \(p\) 減去牌面上的數字。
  • 乘法牌,一共只有 \(1\) 種: \(C_2\) 。它的作用效果是令 \(p\) 乘上對應的數字,即 \(p\gets p\times x\)
  • 除法牌,同樣只有 \(1\) 種: \(D_2\) 。會令 \(p\) 除以對應的數字,向下取整。即 \(p\gets \lfloor p\div x\rfloor\)
  • 固定牌,一共有 \(3\) 種: \(E_{0},E_{49},E_{99}\) ,會將 \(p\) 直接設定為牌面上的數字。

解牌

解牌是可以使一名玩家跳過該回合,並附加一些特殊效果的一類牌。

  • \(\tt{PASS}\) ,跳過你,轉到下一個玩家。
  • \(\tt{TURN}\) ,跳過你,出牌順序反轉(順時針變為逆時針,逆時針變為順時針。在下一輪遊戲開始時會重置為順時針)。
  • \(\tt{DOUBLE}\) ,跳過你,然後給下一名玩家施加 \(\verb!"DOUBLE"!\) 效果,也即要出兩張牌(先打一摸一,再打一摸一,需要保持全程總數不超過 \(99\) 才能保證不失敗)。

\(\tt{DOUBLE}\) 效果的一些說明:如果你被施加了 \(\tt{DOUBLE}\) 的效果,但是你第一張出瞭解牌(三種解牌都可以),那麼你就會立即解除 \(\tt{DOUBLE}\) 效果,跳過這一回合,並且將效果轉移到下一名玩家\(\tt{DOUBLE}\) 效果不能疊加。


在輸入檔案中,卡牌名會形如 \(\colorbox{#f0f0f0}\verb!A1 A99 D2 PASS DOUBLE!\) 等等。

策略

這一部分將會講述本題中所有玩家的執行邏輯。

如果無論怎麼出都會失敗,那麼玩家就會隨便打出一張牌併成為失敗者(顯然,打出哪張牌不會對遊戲結局產生實質上的影響)。否則會有兩種情形:

  1. 如果此時沒有被施加 \(\tt{DOUBLE}\) 效果:
    • 每名玩家會優先考慮普通牌,並且選擇在不成為失敗者的前提下使 \(p\) 變得儘可能大的那種方案(如果有多種方案可以使得 \(p\) 最大,那就會按照乘法牌、加法牌、減法牌、除法牌、固定牌的順序優先選擇。顯然,同一類普通牌中的不同種類的牌不會使 \(p\) 產生相同的值)。
    • 如果沒有普通牌,或者出牌後會成為失敗者,那麼就考慮使用解牌。玩家會依次考慮手頭是否有 \(\tt{PASS,TURN,DOUBLE}\) 牌。如果有,就打出這張牌。
  2. 如果被施加了 \(\tt{DOUBLE}\) 效果:
    • 優先考慮使用解牌。依次考慮\(\tt{PASS,TURN,DOUBLE}\) 。如果有,就打出這張牌。
    • 否則,選擇在不成為失敗者的前提下使 \(p\) 變得儘可能小的那種方案(如果有多種方案可以使得 \(p\) 最小,那就會按照除法牌、減法牌、加法牌、乘法牌、固定牌的順序優先選擇)。此時玩家會被解除 \(\tt{DOUBLE}\) 狀態,於是她會按照情形 \(1\) 來決策。

輸入格式

第一行三個正整數 \(n,m,k\) ,含義如題面所示。

接下來 \(n\) 行,每行四個字串 \(name,card_1,card_2,card_3\) ,表示這名玩家的名稱以及她的三張手牌。玩家名稱僅由大小寫英文字母組成,不包含空格,長度不超過 \(20\)

接下來一行, \(k\) 個字串,按照從上至下的順序描述牌堆裡的牌。

詳情可以參考輸入樣例。

輸出格式

當新的一輪開始時,你需要輸出: \(\colorbox{f0f0f0}\verb!Round XXX:!\) (其中,\(\verb!XXX!\) 表示這是第多少輪)。

每名玩家出牌時,如果她不是失敗者,你需要輸出: \(\colorbox{f0f0f0}\verb!XXX used YYY,now p=ZZZ.!\) (其中,\(\verb!XXX!\) 是玩家名; \(\verb!YYY!\) 是卡牌名; \(\verb!ZZZ!\) 是當前 \(p\) 的值)。

否則,當一名玩家成為失敗者時,該輪結束。你要輸出:\(\colorbox{f0f0f0}\verb!XXX lost the game.!\) (其中,\(\verb!XXX!\) 是玩家名)。

詳情可以參考輸出樣例。

樣例 #1

樣例輸入 #1

2 1 10
JoesSR B9 A99 PASS
Cirno C2 D2 A49
E49 DOUBLE PASS A19 A49 A99 A99 A99 A99 A99

樣例輸出 #1

Round 1:
JoesSR used A99,now p=99.
Cirno used D2,now p=49.
JoesSR used E49,now p=49.
Cirno used C2,now p=98.
JoesSR used B9,now p=89.
Cirno used DOUBLE,now p=89.
JoesSR used PASS,now p=89.
Cirno lost the game.

樣例 #2

樣例輸入 #2

3 2 25
Cirno A9 A19 B1
Reimu TURN A9 C2
Marisa DOUBLE D2 D2
A9 B9 C2 PASS PASS A9 A1 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99 A99

樣例輸出 #2

Round 1:
Cirno used A19,now p=19.
Reimu used C2,now p=38.
Marisa used D2,now p=19.
Cirno used A9,now p=28.
Reimu used A9,now p=37.
Marisa used C2,now p=74.
Cirno used A9,now p=83.
Reimu used B9,now p=74.
Marisa used A9,now p=83.
Cirno used A1,now p=84.
Reimu used PASS,now p=84.
Marisa used D2,now p=42.
Cirno used B1,now p=41.
Reimu used TURN,now p=41.
Cirno used PASS,now p=41.
Marisa used DOUBLE,now p=41.
Reimu lost the game.
Round 2:
Reimu used A99,now p=99.
Marisa lost the game.

提示

樣例 1 說明

牌的使用情況都在輸出樣例中。這裡僅說明每出一張牌後每名玩家當前手牌的情況。具體為什麼要使用某張牌,可以參考題目描述。

:初始回合以及第 \(2,4,6\) 回合都是 \(\text{JoesSR}\) 出牌;第 \(1,3,5,7\) 回合都是琪露諾出牌。值得注意的是,儘管第 \(5\) 回合琪露諾使用了 \(\tt{DOUBLE}\) ,但因為下一回合被 \(\tt{PASS}\) 了,所以第 \(7\) 回合仍然是琪露諾出牌。

此時琪露諾無論如何都會失敗,於是琪露諾成為了失敗者。

樣例 3

見下發附件。

資料規模與約定

  • 對於 \(30\%\) 的資料,僅包含普通牌,並且 \(n\le 3\)
  • 對於另外 \(15\%\) 的資料,不包含 \(\tt{TURN}\) 牌和 \(\tt{PASS}\) 牌。
  • 對於另外 \(15\%\) 的資料,不包含 \(\tt{DOUBLE}\) 牌。
  • 對於 \(100\%\) 的資料, 滿足 \(1\le n\le 30;1\le m\le 100;1\le k\le 3\times 10^5\) 。保證任何時候 \(|p|<10^4\)

參考資料

【東方桌遊考古】NEU,“⑨牌”,與十年前的同人

我碼力太差了,凹了半天,在\(9G\)幫助下終於凹完了,
細節很多
下面總結一下出錯的地方:

  1. \(DOUBLE\)和無\(DOUBLE\)的選擇優先順序是不一樣的
  2. 注意初始化
  3. 像這種大模擬程式碼儘量越簡潔越好,勤寫註釋,多用函式
  4. \(p\)有可能是負數,所以要注意取整問題,\(C++\)預設負數是向\(0\)取整,但我們並不想要這樣
點選檢視程式碼
#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 3e5+5;
int n,m,k,p,pai=1,cao=1;
char K[N][15],doub;
char mp[10]={'C','A','B','D','E','P','T','O'};
char mp1[10]={'D','B','A','C','E','P','T','O'};
bool lsd;
struct pp
{
	string name;
	char op[5][15];
	void comp1()
	{
		char op1[5][15]={};
		int cnt=1;
		for(int k=0;k<8;k++)
		{
//			if(cnt==4)break;
			for(int i=1;i<=3;i++)
			{
//				cout<<k<<" "<<mp[k]<< ' '<<op[i][2]<<" "<<endl;
				if((k==7&&op[i][2]==mp[k]))
				{
					memcpy(op1[cnt],op[i],sizeof op[i]);
					cnt++;
				}else if(k!=7&&op[i][1]==mp[k]&&op[i][2]!='O')
				{
					memcpy(op1[cnt],op[i],sizeof op[i]);
					cnt++;
				}				
			}
			
		}
		memcpy(op,op1,sizeof op1);
	}
	void comp2()
	{
		char op1[5][15]={};
		int cnt=1;
		for(int k=0;k<8;k++)//按優先順序比較 
		{
//			if(cnt==4)break;
			for(int i=1;i<=3;i++)
			{
//				cout<<k<<" "<<mp[k]<< ' '<<op[i][2]<<" "<<endl;
				if((k==7&&op[i][2]==mp1[k]))//DOUBELE會與Dx衝突 
				{
					memcpy(op1[cnt],op[i],sizeof op[i]);
					cnt++;
				}else if(k!=7&&op[i][1]==mp1[k]&&op[i][2]!='O')
				{
					memcpy(op1[cnt],op[i],sizeof op[i]);
					cnt++;
				}				
			}
			
		}
		memcpy(op,op1,sizeof op1);
	}
}pl[35];

ll op(char *s)
{
	ll tmp=0;
	if(s[3]>='0'&&s[3]<='9')
	{
		tmp=(s[2]-'0')*10+(s[3]-'0');
	}else tmp=(s[2]-'0');
	if(s[1]=='A')
	{
		return p+tmp;
	}else if(s[1]=='B')
	{
		return p-tmp;
	}else if(s[1]=='C')
	{
		return p*2;
	}else if(s[1]=='D'&&s[2]!='O')
	{
		return floor(1.0*p/2.0);//向下取整 
	}else if(s[1]=='E')
	{
		return tmp;
	}else 
	{
		if(s[1]=='D'&&s[2]=='O')return -1e9-1;//這裡值不能太小,因為p可能為負數 
		if(s[1]=='P')return -1e9-2;
		if(s[1]=='T')return -1e9-3;
	}
	return 0;
}
void getcard(int x)
{
	for(int i=1;i<=3;i++)
	{
		cout<<pl[x].op[i]+1<<" ";
	}
	cout<<endl;
}
ll work(int i) 
{
	int id=0;
	for(int j=1;j<=3;j++)
	{
		int tmp=op(pl[i].op[j]);
		if(tmp<-1e9)
		{
			id=j;
			doub=pl[i].op[j][1];
			break;
		}					
	}
	return id;
}
void get(int i,int id)//摸一張牌 
{
	memcpy(pl[i].op[id],K[pai],sizeof K[pai]);pai++;	
	pl[i].comp1();	
}
void jd()//PASS情況不用管,DOUBLE情況要標記 
{
	if(doub)
	{
		if(doub=='T')
		{
			cao=-cao;
		}
		else if(doub=='D') 
		{
			lsd=1;
		}
		doub=0;	
	}
}
int main()
{
//	freopen("A.in","r",stdin);
//	freopen("AA.out","w",stdout);
//	cout<<"((("<<endl;
	cin>>n>>m>>k;
	int st=1;
	for(int i=1;i<=n;i++)
	{
		cin>>pl[i].name>>pl[i].op[1]+1>>pl[i].op[2]+1>>pl[i].op[3]+1;
		pl[i].comp1();//排序 
	}
	for(int i=1;i<=k;i++)cin>>K[i]+1;
	for(int rd=1;rd<=m;rd++)
	{
		printf("Round %d:\n",rd);
		cao=1;p=0; //注意初始化 
		doub=0;//當前解牌 
		lsd=0;//是否有double效果 
		for(int i=st;;i+=cao)
		{
			
			if(i>n)i=1;
			if(i==0)i=n;
//			cout<<i<<endl;
//			getcard(i);
			ll ans=-1e9;
			int id=0;
			if(lsd)
			{
//				cout<<"*****"<<endl;
				id=work(i);//先查詢解牌 
				if(!id)
				{
					pl[i].comp2();
					lsd=0; ans=1e9; 
					for(int j=1;j<=3;j++)
					{
						int tmp=op(pl[i].op[j]);
						if(tmp>-1000000000ll&&tmp<=99&&tmp<ans)//注意這裡是找最小值 
						{
							ans=tmp;
							id=j;						
						}
					}
					if(!id)//一定要注意判斷輸了的情況 
					{
						st=i;
						cout<<pl[i].name<<" lost the game."<<endl;
						memcpy(pl[i].op[1],K[pai],sizeof K[pai]);pai++;
						memcpy(pl[i].op[2],K[pai],sizeof K[pai]);pai++;
						memcpy(pl[i].op[3],K[pai],sizeof K[pai]);pai++;
						pl[i].comp1();
						break;
					}
					if(id)p=ans;
//					cout<<"&&&"<<ans<<endl;
					cout<<pl[i].name<<" used "<<pl[i].op[id]+1<<",now p="<<p<<"."<<endl;
					get(i,id); 		
				}
				else
				{
					cout<<pl[i].name<<" used "<<pl[i].op[id]+1<<",now p="<<p<<"."<<endl;
					get(i,id); jd();
					continue;
				}
			}
			ans=-1e9; id=0;
//			getcard(i);
			for(int j=1;j<=3;j++)
			{
				ll tmp=op(pl[i].op[j]);
//				cout<<tmp<<" "<<p<<" "<<pl[i].op[j]+1<<endl;
				if(tmp>-1000000000ll&&tmp<=99&&tmp>ans)//正常先找普通牌,沒有的話再找解牌 
				{
					ans=tmp;
					id=j;
				}
				
			}//common 
			if(id)p=ans;
			if(!id)
			{
				id=work(i);
			}//special	
//			cout<<id<<endl;	
			if(!id)
			{
				st=i;
				cout<<pl[i].name<<" lost the game."<<endl;
				memcpy(pl[i].op[1],K[pai],sizeof K[pai]);pai++;
				memcpy(pl[i].op[2],K[pai],sizeof K[pai]);pai++;
				memcpy(pl[i].op[3],K[pai],sizeof K[pai]);pai++;
				pl[i].comp1();
				break;
			}
			cout<<pl[i].name<<" used "<<pl[i].op[id]+1<<",now p="<<p<<"."<<endl;
			get(i,id);
			jd();
		}
	}
	return 0;
}

造資料程式

點選檢視程式碼
#include<bits/stdc++.h>
#define rand(a,b) (rand()%((b)-(a)+1)+(a))
using namespace std;
typedef long long ll;
string card[]={"","A1","A2","A5","A9","A19","A49","A99","B1","B9","B19","C2","D2","E0","E49","E99","PASS","TURN","DOUBLE"};
string name[]={"","Player1","Player2","Player3","Player4","Player5"};
int main(){
	freopen("A.in","w",stdout);
	srand(time(0));
	int n=rand(2,5),m=1,k=3e5;
	cout<<n<<" "<<m<<" "<<k<<"\n";
	for(int i=1;i<=n;i++){
		cout<<name[i]<<" ";
		for(int j=1;j<=3;j++){
			cout<<card[rand(1,18)]<<" ";
		}
		cout<<"\n";
	}
	while(k--){
		cout<<card[rand(1,18)]<<" ";
	}
	return 0;
}

Minesweeper 1D

題面翻譯

給定一個串,由 012*? 組成。求將?替換成其他字元中的任意一個,使原串合法的方案數。將*視為雷,數字就是數字,合法為掃雷地圖的合法性。

PS:數字 \(i\) 表示以 \(i\) 為中心的九宮格中只有 \(i\) 個雷。如 2* 是非法的,而 *2* 是合法的。

題目描述

Game "Minesweeper 1D" is played on a line of squares, the line's height is 1 square, the line's width is $ n $ squares. Some of the squares contain bombs. If a square doesn't contain a bomb, then it contains a number from 0 to 2 — the total number of bombs in adjacent squares.

For example, the correct field to play looks like that: 001*2***101*. The cells that are marked with "*" contain bombs. Note that on the correct field the numbers represent the number of bombs in adjacent cells. For example, field 2* is not correct, because cell with value 2 must have two adjacent cells with bombs.

Valera wants to make a correct field to play "Minesweeper 1D". He has already painted a squared field with width of $ n $ cells, put several bombs on the field and wrote numbers into some cells. Now he wonders how many ways to fill the remaining cells with bombs and numbers are there if we should get a correct field in the end.

輸入格式

The first line contains sequence of characters without spaces $ s_{1}s_{2}...\ s_{n} $ $ (1<=n<=10^{6}) $ , containing only characters "*", "?" and digits "0", "1" or "2". If character $ s_{i} $ equals "*", then the $ i $ -th cell of the field contains a bomb. If character $ s_{i} $ equals "?", then Valera hasn't yet decided what to put in the $ i $ -th cell. Character $ s_{i} $ , that is equal to a digit, represents the digit written in the $ i $ -th square.

輸出格式

Print a single integer — the number of ways Valera can fill the empty cells and get a correct field.

As the answer can be rather large, print it modulo $ 1000000007 $ $ (10^{9}+7) $ .

樣例 #1

樣例輸入 #1

?01???

樣例輸出 #1

4

樣例 #2

樣例輸入 #2

?

樣例輸出 #2

2

樣例 #3

樣例輸入 #3

**12

樣例輸出 #3

0

樣例 #4

樣例輸入 #4

1

樣例輸出 #4

0

提示

In the first test sample you can get the following correct fields: 001**1, 001***, 001*2*, 001*10.

線性DP,設\(dp_{i,0/1/2}\),表示第\(i\)位的後一位(0不是雷,1是雷),\(2\)表示自身是雷

點選檢視程式碼
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+5,mod=1e9+7;
char s[N];int len,ans;
int dp[N][6];
int main()
{
//	freopen("B.in","r",stdin);
	cin>>s+1;
	len=strlen(s+1);
	dp[0][0]=1;dp[0][1]=1;
	for(int i=1;i<=len;i++)
	{
		if(s[i]=='?')
		{
			dp[i][2]+=dp[i-1][1]+dp[i-1][2];
			dp[i][0]+=dp[i-1][0]+dp[i-1][2];
			dp[i][1]+=dp[i-1][0]+dp[i-1][2];
		}
		else if(s[i]=='1')
		{
			dp[i][1]+=dp[i-1][0];
			dp[i][0]+=dp[i-1][2];
		}
		else if(s[i]=='2')
		{
			dp[i][1]+=dp[i-1][2];
		}
		else if(s[i]=='*')
		{
			dp[i][2]+=dp[i-1][2]+dp[i-1][1];	
			
		}else if(s[i]=='0')
		{
			dp[i][0]+=dp[i-1][0];
		}
		dp[i][0]%=mod;dp[i][1]%=mod;dp[i][2]%=mod;		
	}
	cout<<(dp[len][2]+dp[len][0])%mod<<endl;
	return 0;
}

Description
已知兩個數 x,yx,y 求有多少個正整數不能被 \(a\times x+b\times y,a\ge0,b\ge0a×x+b×y,a≥0,b≥0\) 表示

Input Format
一行兩個整數表示 x,yx,y

Output Format
一行一個整數表示答案,若有無窮個數無法被表示,輸出-1

Sample
樣例輸入
2 3
樣例輸出
1
Hint
對於全部測試點,\(1\le x,y\le10^81≤x,y≤10\)
8

子任務 1(10 分):\(1\le x,y\le10001≤x,y≤1000\)

子任務 2(20 分):\(|y-x|=1∣y−x∣=1\)

子任務 3(30 分):x,yx,y 互質

子任務 4(40 分):無特殊性質

首先子任務\(3\)的部分分很好拿

if(x>y)swap(x,y);
ll n=x-2;
ll ans=x-1+n*(n+1)/2;

然後我們發現如果兩個數不互質,那麼只能是\(lcm(x,y)\)的倍數,一定能過輸出\(-1\)
在考慮兩數互質時,
我們知到,若\(a_i\)構成模\(n\)的完系,\(k,m \in Z (m,n)=1_,\)\(k+ma_i\),也
構成模\(n\)的完系.
我們可以考慮模\(b\)時什麼時候\([1,b-1]\)出現,當一個餘數第一次出現,以後一定可以出現
在互質的條件下,本題中\(x\)可以變相的寫成\(k+ay\)的形式,那麼\([1,y-1]\)都會從\(i\times (k+ay)\mod y\)中出現,出現的順序不一樣,但一定都會出現,那麼第一次出現這個餘數之前所有模\(y\)等於這個餘數的數量和即為答案,即\(\sum_{i=1}^{y-1}\frac{ix}{y}\)
然後這是\(O(y)\)的複雜度,可以最佳化成\(O(1)\),就是把上面的式子化成\(ty=\sum ix-\sum (ix \mod y)\)
image

春節十二響

啟發式合併
滿足\(f_i<i\)所以倒序更新就行了

點選檢視程式碼
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e6+5;
ll n,f[N],m[N],t[N],tot;
priority_queue <ll> q[N];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>m[i];
	for(int i=2;i<=n;i++)cin>>f[i];	
	for(int i=n;i>1;i--)
	{
		int x=i;int y=f[i];q[x].push(m[x]);tot=0;
		if(q[x].size()>q[y].size())swap(q[x],q[y]);
		while(!q[x].empty())
		{
			t[++tot]=max(q[x].top(),q[y].top());
			q[x].pop();q[y].pop();
		}
		for(int j=1;j<=tot;j++)
		{
			q[y].push(t[j]);
		}
	}
	ll ans=m[1];
	while(q[1].size())
	{
		ans+=q[1].top();
		q[1].pop();
	}
	cout<<ans;
	return 0;
}

點選檢視程式碼
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e6+5;
ll n,f[N],m[N],t[N],tot;
priority_queue <ll> q[N];
vector <int> edge[N];
void merge(int x,int y)
{
	tot=0;
	if(q[x].size()>q[y].size())swap(q[x],q[y]);
	while(!q[x].empty())
	{
		t[++tot]=max(q[x].top(),q[y].top());
		q[x].pop();q[y].pop();
	}
	for(int i=1;i<=tot;i++)q[y].push(t[i]);
}
void dfs(int u)
{
	for(auto to:edge[u])
	{
		dfs(to);
		merge(to,u);//統一合併到根節點 
	}
	q[u].push(m[u]);//一定要放最後,他不能與to放在一塊 
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>m[i];
	for(int i=2;i<=n;i++)cin>>f[i],edge[f[i]].push_back(i);	//建邊 
	dfs(1);
	ll ans=0;
	while(q[1].size())
	{
		ans+=q[1].top();
		q[1].pop();
	}
	cout<<ans;
	return 0;
}

相關文章