1. 程式人生 > 其它 >聯合省選2022題解

聯合省選2022題解

Day1

T1 前處理器 preprocessor

link

題意

模擬 C++ 的 #define 的展開。具體細節參見題面。

資料範圍:行數 \(n\le 100\),每行長度 \(\le 100\),答案的每行長度 \(len \le 1000\)

solution

直接大力模擬即可通過官方資料,不過如果常數寫的醜的話在 hack 資料上可能會 T。

暴力模擬的化複雜度是 \(O(n^3len)\) 的,不過常數很小。因為可能需要 \(O(n^2)\) 的複雜度來確定答案串的一位。

如果一直不能確定答案的一位,一定是存在下面這種情況

#define A B
#define B C
#define C D
.....
#define Z xxx
A

其中 ABC,...,Z 是一堆長度為 \(O(n)\) 的串。

字串 A \(\to\) B 的變化需要 \(O(n)\) 的代價。所以我們把這一過程改為雜湊值的變化,每次變化就是 \(O(1)\) 的了。

這樣總複雜度是 \(O(n^2len)\)

view code
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int s=0,f=1;
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;ch=getchar();
	}
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	while(ch!='\n')ch=getchar();
	return s*f;
}
inline bool check(char x){
	if(x>='A'&&x<='Z')return 1;
	if(x>='a'&&x<='z')return 1;
	if(x>='0'&&x<='9')return 1;
	if(x=='_')return 1;
	return 0;
}
#define ll long long
const int mod1=998244353,mod2=1e9+7;
inline ll gethsh(const string&s){
	int ret1=0,ret2=0;
	for(char x:s){
		ret1=(233ll*ret1+x)%mod1;
		ret2=(233ll*ret2+x)%mod2;
	}
	return 1000000000ll*ret1+ret2;
}
const int N=1005;
unordered_map<ll,string> name;
unordered_map<ll,string> to;
unordered_map<ll,string> S[N];
unordered_map<ll,int> d;
unordered_map<ll,int> id;
unordered_map<ll,ll> to1;
bool vis[N],previs[N][N];
inline string bfs(string x,ll hsh){
	if(!id.count(hsh)||!d[hsh]||vis[id[hsh]])return x;
	ll cur=hsh,nxt=to1[hsh];
	while(id.count(nxt)){
		if(vis[id[nxt]]||!d[nxt])
			break;
		vis[id[nxt]]=1;cur=nxt;nxt=to1[cur];
	}
	return to[cur];
}
inline string solve(string s,int dep){
	string st=s;
	memcpy(previs[dep],vis,101);
	if(dep)s=bfs(s,gethsh(s));
	int n=s.size(),l=0;
	S[dep].clear();
	s.push_back('\n');
	string ret;
	for(int i=0;i<=n;++i){
		if(!check(s[i])){
			if(l!=i){
				string t;
				for(int j=l;j<i;++j)
					t.push_back(s[j]);
				ll hsh=gethsh(t);
				if(id.count(hsh)){
					if(!vis[id[hsh]]&&d[hsh]){
						vis[id[hsh]]=1;
						if(S[dep].count(hsh)){
							t=S[dep][hsh];
						}else{
							t=solve(to[hsh],dep+1);
							S[dep][hsh]=t;
						}
						vis[id[hsh]]=0;
					}
				}
				ret+=t;
			}
			if(s[i]!='\n')ret.push_back(s[i]);
			l=i+1;
		}
	}
	memcpy(vis,previs[dep],101);
	return ret;
}
string s;
int n;
int main(){
	n=read();
	for(int i=1;i<=n;++i){
		getline(cin,s);
		if(s.size()==0){
			puts("");
			continue;
		}
		if(s[0]=='#'){
			string t;
			if(s[1]=='u'){
				for(int j=7;j<s.size();++j){
					if(check(s[j]))
						t.push_back(s[j]);
					else break;
				}
				ll hsh=gethsh(t);
				d[hsh]=0;
			}else{
				int pos=0;
				for(int j=8;j<s.size();++j){
					if(check(s[j]))
						t.push_back(s[j]);
					else{
						pos=j+1;
						break;
					}
				}
				ll hsh=gethsh(t);
				d[hsh]=1;
				id[hsh]=i;
				name[hsh]=t;
				t.clear();
				for(int j=pos;j<s.size();++j)
					t.push_back(s[j]);
				to[hsh]=t;
				ll hsh1=gethsh(t);
				to1[hsh]=hsh1;
			}
			puts("");
		}else{
			string ans=solve(s,0);
			cout<<ans<<endl;
		}
	}
	return 0;
}

T2 填樹 tree

link

題意

給定一棵樹,每個點的點權要麼是 \(0\),要麼是 \([l_u,r_u]\) 之內的一個整數。現在已知樹上所有點權非 \(0\) 的點構成一條路徑,且非 \(0\) 權值的極差 \(\le k\)

求滿足條件的給每個點分配點權的方案數 \(\bmod 10^9+7\),以及所有方案中的所有點的點權和 \(\bmod 10^9+7\)

資料範圍\(n\le 200,1\le l_i\le r_i\le 10^9,1\le k\le 10^9\)

solution

設值域為 \(V\),先考慮一個 \(O(nV)\) 的暴力:列舉最大值 \(mx\),求所有方案中以 \(mx\)

為最大值的方案數和所有方案中的點權和。

\(f_{u,0/1}\) 表示以 \(u\) 為端點,路徑上是否有一個點的點權 \(=mx\) 的路徑數量。\(g_{u,0/1}\) 表示 \(f_{u,0/1}\) 中所有路徑的權值和。

初值設為 \(uf_{u,0/1},ug_{u,0/1}\)

那麼合併 \(u,v\) 時,對答案的貢獻是

\[ans1\gets f_{u,x}\times f_{v,y}(x=1\lor y=1)\\ ans2\gets f_{u,x}\times g_{v,y}+g_{u,x}\times f_{v,y}(x=1\lor y=1) \]

轉移有:

\[f_{u,x|y}\gets uf_{u,x}\times f_{v,y}\\ g_{u,x|y}\gets uf_{u,x}\times g_{v,y}+ug_{u,x}\times f_{v,y} \]

即把 \(v\) 為端點的所有路徑接上一個 \(u\)

容易發現,\(uf_{u,0/1}\) 是一個關於 \(mx\) 的分段一次函式:

  • \(mx\in [0,l_u)\)
  • \(mx\in [l_u,r_u]\)
  • \(mx\in (r_u,l_u+k]\)
  • \(mx\in (l_u+k,r_u+k]\)
  • \(mx\in (r_u+k,+\infty)\)

先是 \(0\),然後以斜率為 \(1\) 的速度增長,然後取得最大值(\(k\)\(r_u-l_u+1\)),然後以斜率為 \(-1\) 的速度減小,然後變為 \(0\)

同理,\(ug_{u,0/1}\) 也是一個關於 \(x\) 的分段函式,只不過是一個分段的二次函式。

這樣 \(ans1\) 是一個關於 \(mx\)\(n\) 次多項式,\(ans2\) 是一個關於 \(mx\)\(n+1\) 次多項式。

把所有點的分段情況合併起來,就是分成 \(O(n)\) 段,每段是一個不超過 \(n+1\) 次的多項式。我們要求的是多項式的字首和。一個 \(m\) 次多項式的字首和是一個 \(m+1\) 次多項式。所以我們需要一個 \(n+2\) 次的多項式。求出 \(n+3\) 個點值然後拉格朗日插值即可。如果這一段值域裡沒有 \(n+3\) 個數就直接暴力。

複雜度 \(O(n^3)\)

view code
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	return s*f;
}
#define ll long long
const int mod=1e9+7,N=205;
inline int quick_pow(int a,int b){
	int ret=1;
	for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ret=1ll*ret*a%mod;
	return ret;
}
inline int Inv(int a){return quick_pow(a,mod-2);}
inline int add(int a,int b){return (a+b>=mod)?a+b-mod:a+b;}
inline int dec(int a,int b){return (a-b<0)?a-b+mod:a-b;}
inline void Add(int &a,int b){a+=(a+b>=mod)?b-mod:b;}
inline void Dec(int &a,int b){a-=(a-b<0)?b-mod:b;}
int fac[N];
namespace lagrange{
	int x[N],y[N],tot;
	inline void clear(){
		tot=0;
	}
	inline void Set(int _x,int _y){
		x[++tot]=_x;y[tot]=_y;
	}
	inline int calc(int k){
		int ret=0,prod=1;
		fac[0]=1;
		for(int i=1;i<=tot;++i)prod=1ll*prod*dec(k,x[i])%mod,fac[i]=1ll*fac[i-1]*i%mod;
		for(int i=1;i<=tot;++i){
			int fz=1ll*y[i]*prod%mod*Inv(dec(k,x[i]))%mod;
			int fm=1ll*fac[i-1]*fac[tot-i]%mod;
			if((tot-i)&1)fm=mod-fm;
			ret=(ret+1ll*fz*Inv(fm))%mod;
		}
		return ret;
	}
}
int f[N][2],g[N][2];
ll f1[2],g1[2];
int s[N<<1],n,K,l[N],r[N],lim;
vector<int> e[N];
int buc[N*N],tot;
inline int S1(int x){return ((1ll*x*(x+1))>>1)%mod;}
inline int calc1(int l,int r){
	return (l<=r)?r-l+1:0;
}
inline int calc2(int l,int r){
	if(r<l)return 0;
	return dec(S1(r),S1(l-1));
}
int retf,retg,uf[N][2],ug[N][2];
void dfs(int u,int fa,int x){
	if(l[u]<=x&&x<=r[u]){
		f[u][1]=1;
		g[u][1]=x;
	}else{
		f[u][1]=0;
		g[u][1]=0;
	}
	g[u][0]=dec(calc2(max(l[u],x-K),min(r[u],x)),g[u][1]);
	f[u][0]=dec(calc1(max(l[u],x-K),min(r[u],x)),f[u][1]);
	memcpy(uf[u],f[u],sizeof(f[u]));
	memcpy(ug[u],g[u],sizeof(g[u]));
	Add(retf,f[u][1]);
	Add(retg,g[u][1]);
	for(int v:e[u]){
		if(v==fa)continue;
		dfs(v,u,x);
		f1[0]=f1[1]=0;
		g1[0]=g1[1]=0;
		for(int a:{0,1})for(int b:{0,1}){
			f1[a|b]+=1ll*f[u][a]*f[v][b];
			g1[a|b]+=(1ll*g[u][a]*f[v][b]+1ll*f[u][a]*g[v][b]);
		}
		retf=(retf+f1[1])%mod;
		retg=(retg+g1[1])%mod;
		f1[0]=f1[1]=0;
		g1[0]=g1[1]=0;
		for(int a:{0,1})for(int b:{0,1}){
			f1[a|b]+=1ll*uf[u][a]*f[v][b];
			g1[a|b]+=(1ll*ug[u][a]*f[v][b]+1ll*uf[u][a]*g[v][b]);
		}
		f[u][0]=(f[u][0]+f1[0])%mod;
		f[u][1]=(f[u][1]+f1[1])%mod;
		g[u][0]=(g[u][0]+g1[0])%mod;
		g[u][1]=(g[u][1]+g1[1])%mod;
	}
}
int F1[N],G1[N];
int main(){
	n=read();K=read();
	int mxr=0;
	for(int i=1;i<=n;++i){
		l[i]=read();r[i]=read();
		buc[++tot]=l[i]-1;
		buc[++tot]=r[i];
		buc[++tot]=l[i]+K;
		buc[++tot]=r[i]+K;
		mxr=max(mxr,r[i]);
	}
	sort(buc+1,buc+1+tot);
	tot=unique(buc+1,buc+1+tot)-buc-1;
	for(int i=1,u,v;i<n;++i){
		u=read();v=read();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	int F=0,G=0;
	for(int o=2;o<=tot;++o){
		int L=buc[o-1]+1,R=buc[o];
		if(L>mxr)break;
		if(R-L+1<=n+3){
			for(int i=L;i<=R;++i){
				retf=retg=0;
				dfs(1,0,i);
				Add(F,retf);
				Add(G,retg);
			}
			continue;
		}
		for(int i=L;i<=L+n+2;++i){
			retf=retg=0;
			dfs(1,0,i);
			F1[i-L]=retf;
			G1[i-L]=retg;
		}
		lagrange::clear();
		for(int i=0;i<=n+2;++i){
			if(i)Add(F1[i],F1[i-1]); 
			lagrange::Set(L+i,F1[i]);
		}
		Add(F,lagrange::calc(R));
		
		lagrange::clear();
		for(int i=0;i<=n+2;++i){
			if(i)Add(G1[i],G1[i-1]); 
			lagrange::Set(L+i,G1[i]);
		}
		Add(G,lagrange::calc(R)); 
	}
	printf("%d\n%d\n",F,G);
	return 0;
}

T3 學術社群 community

link

題意

\(n\) 個人,總共發出 \(m\) 條訊息。每條訊息是下面 \(3\) 種中的一種。

  • A:B 樓上,稱為樓上型訊息,\(A\) 為傳送人
  • A:B 樓下,稱為樓下型訊息,\(A\) 為傳送人
  • A: ...,稱為學術訊息,\(A\) 為傳送人

價值的定義如下:

  • 對於樓上型訊息A:B 樓上,如果它的下一條訊息的傳送人是 B,那麼有 \(1\) 的價值,否則沒有價值。
  • 對於樓下型訊息A:B 樓下,如果它的上一條訊息的傳送人是 B,那麼有 \(1\) 的價值,否則沒有價值。
  • 對於學術訊息,沒有價值。

構造一個使得價值最大的重排訊息的方案。保證每個人都發出了至少 \(1\) 條學術訊息。\(T\) 組資料。

資料範圍\(T\le 100,n\le m\le 77777,\sum m\le 2.5\times 10^5\)

solution

先考慮特殊性質 \(C\) 的做法:

把每條限制拆點,對於樓上型訊息 \(x\) A:B 樓上\(x\) 的出點連向所有 \(B\) 的訊息的入點 \(y\),表示如果 \(x\)\(y\) 前面就有 \(1\) 的價值。樓下型訊息類似。這樣變成了一個二分圖匹配問題。現在邊數是 \(O(m^2)\) 的,不過我們連的邊只跟一條訊息的發出人有關,加 \(2n\) 個虛點即可把邊數優化到 \(O(m)\)。現在的圖不是一個二分圖,不過用與二分圖最大匹配相同的複雜度分析方法可知,現在的跑 Dinic 二分圖匹配的複雜度依然是 \(O(m\sqrt{m})\) 的。

不過這樣構造出來的方案可能成環,比如 \(x\to y\to z\to x\)

注意到這個環上有如下兩個性質:

  • 環上的所有訊息要麼都是樓上型訊息,要麼都是樓下型訊息。
  • 環上的所有訊息不包含學術訊息。

我們可以用學術訊息來斷環為鏈。

具體的,\(x\) 的發出人 \(A\) 有一條學術訊息 \(x'\)。設此時 \(x'\) 的連邊關係是 \(p\to x'\to q\)(顯然此時 \(p\) 要麼不存在,要麼是樓上型訊息,\(q\) 要麼不存在,要麼是樓下型訊息)。

  • 如果 \(x,y,z\) 均為樓上型訊息,那麼斷環為鏈的方式是 \(p\to x\to y\to z\to x'\to q\)
  • 如果 \(x,y,z\) 均為樓下型訊息,那麼斷環為鏈的方式是 \(p\to x'\to y\to z\to x\to q\)

跑完 Dinic 之後找到一組匹配,把所有環斷掉即可。複雜度瓶頸在 Dinic,為 \(O(m\sqrt{m})\)


現在沒有了特殊性質 \(C\),直接連邊是不行的,因為A B 樓上B A 樓下 前面的化,價值是 \(2\) 而不是 \(1\),就得跑費用流了。容易發現直接把 A B 樓上B A 樓下 匹配起來一定不會更劣。然後就又變成了上面一樣的問題。

唯一的區別是這些被匹配的訊息只有入點/出點了。

總複雜度 \(O(m\sqrt{m})\)。如果 Dinic 跑不動的化不妨嘗試以下改變建邊順序,能從十幾秒優化到 \(1\) 秒。

view code
#include <bits/stdc++.h>
using namespace std;
namespace iobuff{
	const int LEN=1000000;
	char in[LEN+5],out[LEN+5];
	char *pin=in,*pout=out,*ed=in,*eout=out+LEN;
	inline char gc(void){
		return pin==ed&&(ed=(pin=in)+fread(in,1,LEN,stdin),ed==in)?EOF:*pin++;
	}
	inline void pc(char c){
		pout==eout&&(fwrite(out,1,LEN,stdout),pout=out);
		(*pout++)=c;
	}
	inline void flush(){fwrite(out,1,pout-out,stdout),pout=out;}
	template<typename T> inline void read(T &x){
		static int f;
		static char c;
		c=gc(),f=1,x=0;
		while(c<'0'||c>'9') f=(c=='-'?-1:1),c=gc();
		while(c>='0'&&c<='9') x=10*x+c-'0',c=gc();
		x*=f;
	}
	inline void tostring(string&s){
		s.clear();
		char ch=gc();
		for(int i=1;;++i,ch=gc()){
			if(ch==' '||ch=='\n')break;
			else s.push_back(ch); 
		}
	}
	template<typename T> inline void putint(T x,char div='\n'){
		static char s[20];
		static int top;
		top=0;
		x<0?pc('-'),x=-x:0;
		while(x) s[top++]=x%10,x/=10;
		!top?pc('0'),0:0;
		while(top--) pc(s[top]+'0');
		pc(div);
	}
}
using iobuff::putint;
using iobuff::read;
using iobuff::tostring;
using iobuff::pc;
using iobuff::flush;
namespace Flow{
	const int N=3.2e5+5,M=1e6+5,inf=1e9;
	struct Edge{
		int fr,to,next,flow;
	}e[M];
	int head[N],ecnt=1,tot;
	inline void adde(int u,int v,int flow){
		e[++ecnt]=(Edge){u,v,head[u],flow};head[u]=ecnt;
		e[++ecnt]=(Edge){v,u,head[v],0};head[v]=ecnt;
	}
	inline void init(){
		memset(head+1,0,tot<<2);
		ecnt=1;
		tot=0;
	}
	int dep[N],cur[N],q[N];
	inline bool bfs(int s,int t){
		memset(dep+1,0x3f,tot<<2);
		dep[s]=0;
		int l=1,r=1;q[l]=s;
		while(l<=r){
			int u=q[l++];
			cur[u]=head[u];
			if(t==u)return 1;
			for(int i=head[u],v;i;i=e[i].next){
				if(e[i].flow&&dep[v=e[i].to]>dep[u]+1){
					dep[q[++r]=v]=dep[u]+1;
				}
			}
		}
		return dep[t]<inf;
	}
	inline int dinic(int u,int t,int flow){
		if(u==t)return flow;
		int ret=0,f,v;
		for(int &i=cur[u];i&&flow;i=e[i].next){
			if(dep[v=e[i].to]!=dep[u]+1||!e[i].flow)continue;
			f=dinic(v,t,min(flow,e[i].flow));
			e[i].flow-=f;e[i^1].flow+=f;
			ret+=f;
			flow-=f;
		}
		if(flow)dep[u]=inf;
		return ret;
	}
	inline int maxflow(int s,int t){
		int ret=0;
		while(bfs(s,t))
			ret+=dinic(s,t,inf);
		return ret;
	}
}
const int N=3.2e5+5,M=1e6+5,inf=1e9;
struct Edge{
	int to,next;
}e[M>>1];
int head[N],ecnt,cur[N];
inline void adde(int u,int v){
	e[++ecnt]=(Edge){v,head[u]};head[u]=ecnt;
}
map<string,int> mapp;
int fr[N],to[N],pre[N],nxt[N],n,m;
int ans;
map<pair<int,int>,int> mapp1;
int inde;
inline void init(){
	memset(head+1,0,(2*n+2*m+2)<<2);
	ecnt=0;
	inde=0;mapp1.clear();
} 
inline int gid(int u,int v){
	if(mapp1.count(make_pair(u,v)))return mapp1[make_pair(u,v)];
	else return mapp1[make_pair(u,v)]=++inde;
}
inline int gid1(int u,int v){
	if(mapp1.count(make_pair(u,v)))return mapp1[make_pair(u,v)];
	else return 0;
}
vector<int> buc_down[N];
bool del[N],vis[N];
int deg[N],Nxt[N],ed[N],st[N],Pre[N];
inline void Add(int u,int v){
	if(!v||!u)return;
	Nxt[u]=v;
	Pre[v]=u;
}
inline void Cut(int u,int v){
	if(!v||!u)return;
	Nxt[u]=Pre[v]=0;
}
void solve_notC(){
	for(int i=1;i<=m;++i){
		if(nxt[i]){
			int t=gid1(to[i],fr[i]);
			if(t&&buc_down[t].size()){
				del[i]=1;
				del[*buc_down[t].rbegin()]=1;
				Nxt[i]=*buc_down[t].rbegin();++deg[*buc_down[t].rbegin()];
				Add(i,Nxt[i]);
				buc_down[t].pop_back();
				ans+=2;
			}
		}
	}
}
int dfs(int u){
	if(u==2*n+2*m+2)return u;
	for(int &i=cur[u];i;i=e[i].next){
		int ret=0;
		if(ret=dfs(e[i].to)){
			i=e[i].next;
			if(ret==2*n+2*m+2)return u;
			else return ret;
		}
	}
	return 0;
}
int stac[N],top,fat[N],sz[N],bc[N];
bool instac[N];
void dfs1(int u){
	stac[++top]=u;
	instac[u]=1;
	vis[u]=1;
	int v=Nxt[u];
	if(vis[v]){
		if(instac[v]){
			if(pre[v]==fr[u]){
				int p1=Nxt[v],p2=Nxt[ed[fr[v]]];
				Cut(v,Nxt[v]);
				Cut(ed[fr[v]],p2);
				Add(ed[fr[v]],p1);
				Add(v,p2);
			}else{
				int p2=Pre[ed[fr[v]]];
				Cut(u,v);
				Cut(p2,ed[fr[v]]);
				Add(p2,v);
				Add(u,ed[fr[v]]);
			}
			
		}
	}else if(v){
		dfs1(v);
	}
	instac[u]=0;--top;
}
int main(){
	int T;read(T);
	for(int o=1;o<=T;++o){
		read(n);read(m);
		mapp.clear();
		Flow::init();
		ans=0;
		for(int i=1;i<=n;++i){
			string name;
			tostring(name);
			mapp[name]=i;
		}
		init();
		for(int i=1;i<=m;++i){
			del[i]=0;deg[i]=0;vis[i]=0;Nxt[i]=0;Pre[i]=0;
			buc_down[i].clear();
			string name;
			tostring(name);
			fr[i]=mapp[name];
			tostring(name);
			if(mapp.count(name))to[i]=mapp[name];
			else to[i]=0;
			tostring(name);
			pre[i]=nxt[i]=0;
			if(to[i]&&name=="loushang"){
				nxt[i]=to[i];
			}else if(to[i]&&name=="louxia"){
				pre[i]=to[i];
				buc_down[gid(fr[i],to[i])].push_back(i);
			}else to[i]=0,ed[fr[i]]=i;
			++sz[fr[i]];bc[fr[i]]=i;
		}
		solve_notC();
		int s=2*n+2*m+1,t=s+1;
		Flow::tot=t;
		for(int i=1;i<=m;++i){
			if(!del[i]&&pre[i])
				if(sz[pre[i]]>1)Flow::adde(2*m+pre[i],i+m,1);
				else Flow::adde(bc[pre[i]],i+m,1);
			else if(!del[i]&&nxt[i]){
				if(sz[nxt[i]]>1)Flow::adde(i,2*m+nxt[i]+n,1);
				else Flow::adde(i,m+bc[nxt[i]],1);
			}
			if(!del[i]||!pre[i]){
				if(sz[fr[i]]>1)Flow::adde(2*m+fr[i]+n,i+m,1);
				Flow::adde(i+m,t,1);
			}
			if(!del[i]||!nxt[i]){
				Flow::adde(s,i,1);
				if(sz[fr[i]]>1)Flow::adde(i,2*m+fr[i],1);
			}
		}
		ans+=Flow::maxflow(s,t);
		putint(ans);
		for(int i=2;i<=Flow::ecnt;i+=2)
			if(!Flow::e[i].flow)adde(Flow::e[i].fr,Flow::e[i].to);
		for(int i=1;i<=t;++i)cur[i]=head[i];
		for(int i=1;i<=m;++i){
			int v=dfs(i);
			if(v)
				Nxt[i]=v-m,Add(i,Nxt[i]);
		}
		for(int i=1;i<=m;++i)
			if(!vis[i])
				dfs1(i);
		for(int i=1;i<=m;++i)++deg[Nxt[i]];
		for(int i=1;i<=m;++i){
			if(!deg[i]){
				int u=i;
				for(;u;u=Nxt[u])
					putint(u,' ');
			}
		}
		pc('\n');
	}
	flush();
	return 0;
}

Day 2

T1 卡牌 card

link

題意

\(n\) 個數,第 \(i\) 個數是 \(a_i\)。有 \(m\) 次詢問,每次給出 \(c\) 個質數 \(s_i\),求有多少種選數的方案使得這些數的積能被每個質數整除。答案 \(\bmod 998244353\)

資料範圍\(n\le 10^6,m\le 1500,\sum c\le 18000,1\le a_i,s_i\le 2000\)

solution

一種暴力做法是直接裝壓維護當前出現過的質數集合。注意到值域很小,\(\sqrt{2000}\approx 44.7\) 以內的質數只有 \(14\) 個,而第 \(14\) 個質數 \(43\times 47=2021>2000\),我們暴力維護最小的 \(B=13\) 個質數的出現情況,剩下的大質數最多出現 \(1\) 個。把所有數按照大質數分類,每一類預處理 \(f_s\) 表示出現了 \(s\) 集合內的所有小質數的方案數。這樣最終答案就是一個或卷積的形式。

但是這樣複雜度還是很高,考慮優化。首先考慮 FWT,我們要算的東西應該是若干個 FWT 之後東西對位相乘,然後最後 IFWT 回去。IFWT 的複雜度是 \(O(m2^{B}B)\) 的,可以接受。

如果詢問的質數中不包含 \(x\) 的大質數,那麼 \(x\) 對答案的貢獻是:捲上一個 \(f_0=1,f_x=2^{cnt}-1\)。其中 \(cnt\) 表示 \(n\) 個數中 \(x\) 的出現此次數。我們預處理出所有 \(x\)\(f\) FWT 之後的積,當作詢問的質數中不包含任意一個 \(x\) 的大質數。

如果詢問的質數中包含 \(x\) 的大質數,那麼 \(x\) 對它的大質數的答案的貢獻是:捲上一個 \(g_0=1,g_x=2^{cnt}-1\)。同時,\(x\) 還對上面的答案有貢獻,需要在原來的答案中把它的貢獻除掉。我們預處理出每個大質數的 FWT 結果(即 \(FWT(\prod g)\)),同時預處理出所有 \(x\)\(f\) FWT 之後的積,要在答案中把它除掉。

注意到要卷的東西都只有 \(2\) 項,可以 \(O(2^B)\) 求出 FWT 之後的結果,預處理複雜度 \(O(V2^B)\)\(V\) 是值域。

詢問的時候把詢問的質數中不包含 \(x\) 的大質數的 \(x\) 的貢獻全部算上(即全域性積除掉詢問中出現的大質數的貢獻),然後再把大質數的貢獻算盡來。複雜度 \(O(\sum c2^B)\)

總複雜度 \(O(V2^B+\sum c2^B+m2^BB)\)

view code
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	return s*f;
}
const int N=2005,M=2e4+5,mod=998244353;
const int p1[]={2,3,5,7,11,13,17,19,23,29,31,37,41};
inline int add(int a,int b){return (a+b>=mod)?a+b-mod:a+b;}
inline int dec(int a,int b){return (a-b<0)?a-b+mod:a-b;}
inline void Add(int &a,int b){a+=(a+b>=mod)?b-mod:b;}
inline void Dec(int &a,int b){a-=(a-b<0)?b-mod:b;}
inline int quick_pow(int a,int b){
	int ret=1;
	for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ret=1ll*ret*a%mod;
	return ret;
}
inline int Inv(int a){return quick_pow(a,mod-2);}
int Pow[N*N],cnt[N],n,m,mx,a[N],s[M];
inline void FWT_or(int *f,int len,int x){
	for(int mid=1,k=2;k<=len;mid<<=1,k<<=1)for(int i=0;i<len;i+=k)
		for(int j=0;j<mid;++j)
			x>0?Add(f[i+j+mid],f[i+j]):Dec(f[i+j+mid],f[i+j]);
}
int prime[N],pcnt,F[N>>1][1<<14],prod[N>>1][1<<14],rk[N];
bool vis[N];
inline int gets(int x){
	int ret=0;
	for(int i=0;i<13;++i)
		if(x%p1[i]==0)ret|=(1<<i);
	return ret;
}
inline void init(){
	for(int i=2;i<=mx;++i){
		if(!vis[i])
			prime[++pcnt]=i,rk[i]=pcnt;
		for(int j=1;j<=pcnt&&prime[j]*i<=mx;++j){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
	for(int p=(1<<13)-1;~p;--p)prod[0][p]=1;
	for(int i=1;i<=pcnt;++i){
		for(int p=(1<<13)-1;~p;--p)prod[i][p]=1,F[i][p]=1;
		if(prime[i]<=41)continue;
		for(int j=prime[i];j<=mx;j+=prime[i]){
			int x=gets(j),v=Pow[cnt[j]];
			for(int p=(1<<13)-1;~p;--p)
				if((p&x)==x)F[i][p]=1ll*F[i][p]*v%mod;
		}
		for(int p=(1<<13)-1;~p;--p)Dec(F[i][p],1);
	}
	for(int i=1;i<=mx;++i){
		int x=gets(i),v=Pow[cnt[i]];
		int pos=0;
		for(int j=1;j<=pcnt;++j)
			if(i%prime[j]==0)pos=j;
		if(prime[pos]<=41)pos=0;
		for(int p=(1<<13)-1;~p;--p){
			if((p&x)==x){
				prod[0][p]=1ll*prod[0][p]*v%mod;
				if(pos)prod[pos][p]=1ll*prod[pos][p]*v%mod;
			}
		}
	}
	for(int i=14;i<=pcnt;++i)
		for(int p=(1<<13)-1;~p;--p)prod[i][p]=Inv(prod[i][p]);
}
int main(){
	n=read();
	Pow[0]=1;
	for(int i=1,x;i<=n;++i){
		x=read();++cnt[x];
		Pow[i]=add(Pow[i-1],Pow[i-1]);
		mx=max(mx,x);
	}
	init();
	m=read();
	while(m--){
		int c=read();
		int S=0;
		bool fff=1;
		for(int i=1;i<=c;++i){
			s[i]=read();
			if(s[i]<=41)S|=(1<<(rk[s[i]]-1));
			if(s[i]>mx)fff=0;
		}
		if(!fff){
			puts("0");
			continue;
		}
		sort(s+1,s+1+c);
		c=unique(s+1,s+1+c)-s-1;
		memcpy(F[0],prod[0],sizeof(F[0]));
		for(int i=1;i<=c;++i){
			if(s[i]<=41)continue;
			int x=rk[s[i]];
			for(int j=0;j<(1<<13);++j)
				F[0][j]=1ll*F[0][j]*prod[x][j]%mod*F[x][j]%mod;
		}
		FWT_or(F[0],1<<13,-1);
		int ans=0;
		for(int i=0;i<(1<<13);++i)if((i&S)==S)Add(ans,F[0][i]);
		printf("%d\n",ans);
	}
	return 0;
}

T2 序列變換 bracket

link

題意

給定一個長為 \(2n\) 的合法括號串,每個左括號有一個權值 \(val_i\),你可以進行如下兩種操作:

  • 交換形如 p(A)(B)q 的串中 AB 之間的兩個括號,變換為 p(A()B)q(其中 p, q 為任意串,可以為空,但不一定分別為合法括號序列,AB 必須是合法非空括號序列,下同),它的代價為 \(x\)(A) 中第一個左括號的權值加上 \(y\)(B) 中第一個左括號的權值。
  • 交換形如 pABq 的串中的 AB,變換為 pBAq ,這個操作不需要代價。

其中 \(x,y\in\{0,1\}\),權值會隨著左括號的移動而移動。

你要求最終的括號串中不存在子串 )(,求最小代價。

資料範圍\(n\le 4\times 10^5,1\le val_i\le 10^7\)

solution

觀察這個變化究竟是在幹什麼。

我們把括號樹建出來,稱一個括號在第 \(i\) 層表示它的括號樹上的深度為 \(i\)。那麼我們的操作相當於是:

把第 \(i\) 層的所有括號拿出來,任選這一層的兩對括號 \(A,B\)(因為操作 \(2\) 不花費代價),以 \(x\cdot val_A+y\cdot val_B\) 的代價將括號 \(B\) 扔到下一層去。重複此操作直到這一層只剩下 \(1\) 個括號,把這個括號留在這一層,開始做下一層。

更抽象一點,每一層有若干個數,每次要選擇兩個數 \(a_1,a_2\),以 \(x\cdot a_1+y\cdot a_2\) 的代價將 \(a_2\) 扔到下一層去。每次選擇一個數留在當前層,剩下的都必須要扔到下面一層去。

根據 \(x,y\) 的取值分類討論,設當前層有 \(m\) 個數,最大值為 \(mx\),最小值為 \(mn\),所有數的和是 \(sum\)

  • \(x=1,y=1\)

一共有 \(2(m-1)\) 個代價,每個數都至少對代價貢獻了一次。

那麼貪心的,從次小值開始,每次以 \(a_x+mn\) 的代價把 \(a_x\) 放到下一層,最後用 \(mx+mn\) 的代價把 \(mn\) 放到下一層,\(mx\) 留在當前層。總代價是 \(sum+(m-2)mn\)

隨便使用一個能支援刪除最大值和取最小值的資料結構(比如 multiset) 維護即可。

  • \(x=0,y=1\)

代價是被放到下一層的數的大小,用最大值把所有其他數都方下去即可。代價是 \(sum-mx\)。同樣可以用 multiset/priority_queue 維護。

  • \(x=1,y=0\)

顯然我們希望用最小的代價把所有數都放下去,即用 \(mn\) 的代價把想要方下去的東西方下去。設不放下去的數是 \(c\),再以 \(c\) 的代價把 \(mn\) 放下去即可。(在只剩 \(2\) 個串的時候直接把要放下去用另外一個數放下去即可)

除了最後一層剩下的括號不需要貢獻代價之外,其他所有串都會在被留在當前層時貢獻 \(1\) 的代價。所以貪心的,我們把最大值一直往下放,直到放到最後一層。

所以有貪心:如果當前的集合內有全域性最大值 \(Mx\),我們要確保 \(Mx\) 被放下去,同時我們要確保 \(mn\) 被放了下去(比如在出現了全域性最大值的時候用全域性次大值把 \(mn\) 放進去)。

不過現在有點問題:當前層只有 \(2\) 個括號,那麼只能把 \(Mx\)\(mn\) 中的一個放下去,而我們沒辦法貪心地選擇應該把哪個放下去。

設初始時,每層的數的數量為: \(1,1,1,\cdots,1,2,1,1,\cdots 1,x\)\(x>1\))。

把最開始的一段全是 \(1\) 的先刪掉,變成 \(2,1,1,\cdots 1,x\)。對於 \(x\) 之前的這一段字首,每層在處理的時候都會是 \(2\)\(\to 1\) 個,下一層又加一個進來。所以這一段字首中,每層的數的數量都是 \(2\)。因為每層至少加進來一個數,所以每層的數量是先單調不降,後每次減 \(1\)。所以後面再出現 \(2\) 就是最後一步了,刪掉一個就完了。

我們最終留下來的數一定是這些每層處理 \(2\) 個數的層的最大值/最小值,因為只有這兩個值會影響答案。

分留下最大值/最小值討論,然後做上面的每層都 \(\ge 3\) 個數的貪心。

multiset 維護,複雜度 \(O(n\log n)\)

view code
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	return s*f;
}
const int N=8e5+5;
#define ll long long
char s[N];
int R[N],L[N],stac[N],top,n,x,y,val[N],a[N],cntl[N],cntr[N],dep[N];
vector<int> buc[N];
int mxdep;
ll sl[N];
void build(int l,int r,int depp){
	cntl[l]=cntr[l]=1;
	sl[l]=a[l];
    ++depp;
    dep[l]=dep[r]=depp;
    mxdep=max(mxdep,depp);
    buc[depp].push_back(a[l]);
	if(r<=l+1)return;
	for(int i=l+1;i<r;i=R[i]+1){
		build(i,R[i],depp);
		cntl[l]+=cntl[i];
		cntr[l]+=cntr[i];
		sl[l]+=sl[i];
	}
}
#define ll long long
inline ll work(int cur,int d){
	int mx=max(0,cur);
	for(int i=d;i<=mxdep;++i)
		for(int x:buc[i])mx=max(mx,x);
	multiset<int> s;
	if(cur>=0)s.insert(cur);
	bool flag=0;
	ll ans=0;
	for(int i=d;i<=mxdep||s.size()>1;++i){
		for(int x:buc[i])s.insert(x);
		if(s.size()==2){
			ans+=*s.begin();
			break;
		}
		flag|=(*s.rbegin())==mx;
		if(flag){
			int mm=*prev(s.end());
			s.erase(prev(s.end()));
			ans+=1ll*(*s.begin())*(s.size()-1)+(*s.rbegin());
			s.erase(prev(s.end()));
			s.insert(mm);
		}else{
			ans+=1ll*(*s.begin())*(s.size()-2)+(*s.rbegin());
			s.erase(prev(s.end()));
		}
	}
	return ans;
}
int main(){
	n=read();x=read();y=read();
	if(x==0&&y==0){
		puts("0");
		return 0;
	}
	scanf("%s",s+1);
	s[0]='(';s[n+n+1]=')';
	for(int i=1;i<=n;++i)
		val[i]=read();
	int cnt=-1;
	for(int i=0;i<=n+n+1;++i){
		if(s[i]=='('){
			stac[++top]=i;
			++cnt;
			a[i]=val[cnt];
		}else{
			R[stac[top]]=i;
			L[i]=stac[top];
			--top;
		}
	}
	build(0,n+n+1,-1);
    ll ans=0;
    if(x==1&&y==1){
        multiset<int> s;
        ll sum=0;
        for(int i=1;i<=mxdep||s.size()>1;++i){
			for(int x:buc[i])s.insert(x),sum+=x;
        	if(s.size()>1)ans+=sum+1ll*(*s.begin())*(s.size()-2);
			sum-=*s.rbegin();
			s.erase(prev(s.end()));
		}
		printf("%lld\n",ans);
    }else if(y==1){
		priority_queue<int> q;
        ll sum=0;
        for(int i=1;i<=mxdep||q.size()>1;++i){
			for(int x:buc[i])q.push(x),sum+=x;
        	if(q.size()>1)ans+=sum-q.top();
			sum-=q.top();
			q.pop();
		}
		printf("%lld\n",ans);
	}else{
		int pos1=1;
		for(int i=1;i<=mxdep;++i){
			if(buc[i].size()==1)pos1=i+1;
			else break;
		}
		int pos2=0,mn=1e9,mx=0;
		for(int i=pos1,cnt=0;!pos2;++i){
			cnt+=buc[i].size();
			if(cnt!=2)pos2=i;
			else{--cnt;for(int x:buc[i])mn=min(x,mn),mx=max(x,mx);}
		}
		if(pos1==pos2)
			ans=work(-1,pos2);
		else{
			ans=1e18;
			{
				ll sum=0;
				multiset<int> s;
				for(int i=pos1;i<pos2;++i){
					for(int x:buc[i])s.insert(x);
					if(*s.begin()==mn){
						sum+=*s.rbegin();
						s.erase(prev(s.end()));
					}else{
						sum+=*s.begin();
						s.erase(s.begin());
					}
				}
				ans=min(ans,sum+work(mn,pos2));
			}
			{
				ll sum=0;
				multiset<int> s;
				for(int i=pos1;i<pos2;++i){
					for(int x:buc[i])s.insert(x);
					sum+=*s.begin();
					s.erase(s.begin());
				}
				ans=min(ans,sum+work(mx,pos2));
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T3 最大權獨立集問題

link

題意

給定一個二叉樹,點有點權,刪一條邊的代價是兩個端點的點權和,刪完之後交換兩個點的點權。你需要找到一個刪邊的順序,要把所有邊刪完,最小化代價之和。

資料範圍\(n\le 5000,1\le d_i\le 10^9\)

solution

顯然考慮樹形 DP,先考慮一個暴力的樹形 DP,設 \(f_{u,x,y}\) 表示考慮以 \(u\) 為根,從子樹內換進來的值是 \(d_y\),從子樹外換出去的值是 \(d_x\),把子樹內邊都斷完的最小代代價(算上把 \(d_x\) 換出去的代價)。狀態數就已經是 \(O(n^3)\) 了,無法接受。

第三維是把狀態數變高的罪魁禍首,我們考慮能不能用點別的替換掉它。\(f_{u,x,i}\) 表示考慮以 \(u\) 為根,從子樹外換出去的值是 \(d_x\),從子樹外換進來的數還在字數內被換了 \(i\) 次,把子樹內邊都斷完的最小代代價。如果我們知道是 \(y\) 被換進字數內,代價就是 \(d_y\times i+f_{u,x,i}\)

狀態數看起來還是 \(O(n^3)\) 的,不過對於 \(u\)\(x\),設 \(v\)\(u\) 的兩個兒子中是 \(x\) 的祖先的那一個,\(v'\)\(u\)\(v\) 的兒子,\(f_{u,x,i}\) 的所有 \(i\)\(\le v'\) 子樹的所有點中離 \(u\) 距離的最大值。根據樹形揹包的複雜度分析,現在的狀態數也是 \(O(n^2)\) 的。

然後考慮轉移:

先只考慮一個度數為 \(3\) 的點的轉移(即這個點有左兒子,記為 \(ls\);右兒子,記為 \(rs\),父親,記為 \(fa\))。

  • 斷邊順序為 \(fa,ls,rs\)

此時換出去的值一定是自己,換進 \(rs\) 的值是 \(ls\) 換上來的值,\(fa\) 的值會進 \(ls\) 的子樹,那麼有

\[f_{u,u,i+1}\gets f_{ls,v_1,i}+f_{rs,v_2,j}+d_{v_1}(j+1)+d_u \]

預處理 \(mn_j=\min_{v_2}\{f_{rs,v_2,j}\}\)。列舉 \(v_1\),然後再列舉 \(j\),求出 \(mn2=\min\{mn_j+d_{v_1}(j+1)\}\),再列舉 \(i\),轉移到 \(f_{u,u,i}\gets mn2+f_{ls,v_1,i}+d_u\)。這樣轉移的複雜度和狀態數是一樣的。

  • 斷邊順序為 \(ls,fa,rs\)

此時換出去的值一定是 \(ls\) 換出來的值,換進 \(ls\) 的值是 \(u\) 的值,\(fa\) 的值會進 \(rs\) 的子樹,那麼有

\[f_{u,v_1,j+1}\gets f_{lc,v_1,i}+f_{rc,v_2,j}+d_u(i+1)+d_{v_1} \]

同樣可以優化到 \(O(n^2)\)

  • 斷邊順序為 \(ls,rs,fa\)

此時換出去的值一定是 \(rs\) 換出來的值,換進 \(ls\) 的值是 \(u\) 的值,換進 \(rs\) 的值是 \(ls\) 換上來的值,同時換進來的值會留在 \(u\),那麼有

\[f_{u,v_2,0}\gets f_{lc,v_1,i}+f_{rc,v_2,j}+d_u(i+1)+d_{v_1}(j+1)+d_{v_2} \]

同樣可以優化到 \(O(n^2)\)

  • \(3!\) 中的剩下三種把 \(ls\)\(rs\) 交換再跑一遍即可。

  • 對於當前點為根 (\(1\) 號點)的情況,列舉先刪 \(ls\) 還是 \(rs\),直接對答案做出貢獻。

  • 對於當前點沒有 \(rs\) 的情況:

    • 斷邊順序為 \(fa,ls\)\(f_{u,u,i+1}\gets f_{ls,v_1,i}+d_u\)
    • 斷邊順序為 \(ls,fa\)\(f_{u,v1,0}\gets f_{lc,v_1,i}+d_u(i+1)+d_{v_1}\)

總複雜度 \(O(n^2)\)

view code
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	return s*f;
}
const int N=5005;
vector<int> e[N],son[N];
#define ll long long
#define pb push_back
template<typename T>inline void Max(T&a,T b){if(a<b)a=b;}
template<typename T>inline void Min(T&a,T b){if(a>b)a=b;}
struct Val{
	ll *f;
	int sz=0;
	inline ll& operator[](int x){return f[x];}
	inline void resize(int x){f=new ll[x];sz=x;memset(f,0x3f,x<<3);}
	inline ll gmn(){
		ll ret=1e18;for(int i=0;i<sz;++i)Min(ret,f[i]);
		return ret;
	}
}f[N][N];
int mxdep[N],sz[N],ls[N],rs[N],c[N];
int ch[N][2],gson[N][N];
ll mn1[N],mn2[N];
inline void solve(int u){
	memset(mn1,0x3f,(mxdep[u]+1)<<3);
	for(int v2:son[rs[u]])
		for(int i=0;i<f[rs[u]][v2].sz;++i)
			Min(mn1[i],f[rs[u]][v2][i]);
	for(int v1:son[ls[u]]){
		ll mn=1e18;
		for(int i=0;i<=mxdep[rs[u]];++i)
			Min(mn,mn1[i]+1ll*c[v1]*(i+1));
		for(int i=0;i<f[ls[u]][v1].sz;++i)
			Min(f[u][u][i+1],c[u]+f[ls[u]][v1][i]+mn);
	}
	memset(mn1,0x3f,(mxdep[u]+1)<<3);
	for(int v2:son[rs[u]])
		for(int i=0;i<f[rs[u]][v2].sz;++i)
			Min(mn1[i],f[rs[u]][v2][i]);
	for(int v1:son[ls[u]]){
		ll mn=1e18;
		for(int i=0;i<f[ls[u]][v1].sz;++i)
			Min(mn,f[ls[u]][v1][i]+1ll*c[u]*(i+1));
		for(int i=0;i<=mxdep[rs[u]];++i)
			Min(f[u][v1][i+1],c[v1]+mn+mn1[i]);
	}

	memset(mn1,0x3f,(mxdep[u]+1)<<3);
	for(int v1:son[ls[u]]){
		ll mn=1e18;
		for(int i=0;i<f[ls[u]][v1].sz;++i)
			Min(mn,f[ls[u]][v1][i]+1ll*c[u]*(i+1));
		for(int i=0;i<=mxdep[rs[u]];++i)
			Min(mn1[i],mn+1ll*c[v1]*(i+1));
	}
	for(int v2:son[rs[u]])
		for(int i=0;i<f[rs[u]][v2].sz;++i)
			Min(f[u][v2][0],c[v2]+mn1[i]+f[rs[u]][v2][i]);
}
ll ans=1e18;
void solve1(int u=1){
	memset(mn1,0x3f,(mxdep[u]+1)<<3);
	for(int v2:son[rs[u]])
		for(int i=0;i<f[rs[u]][v2].sz;++i)
			Min(mn1[i],f[rs[u]][v2][i]);
	for(int v1:son[ls[u]]){
		ll mn=1e18;
		for(int i=0;i<=mxdep[rs[u]];++i)
			Min(mn,mn1[i]+1ll*c[v1]*(i+1));
		for(int i=0;i<f[ls[u]][v1].sz;++i)
			Min(ans,mn+f[ls[u]][v1][i]+1ll*c[u]*(i+1));
	}
}
void dfs(int u){
	son[u].pb(u);
	sz[u]=1;
	if(!ls[u]&&!rs[u]){
		f[u][u].resize(1);
		f[u][u][0]=c[u];
		return;
	}
	if(ls[u]){
		int v=ls[u];
		dfs(v);sz[u]+=sz[v];
		mxdep[u]=max(mxdep[v]+1,mxdep[u]);
		for(int x:son[v])son[u].pb(x),gson[u][x]=0;
	}
	if(rs[u]){
		int v=rs[u];
		dfs(v);sz[u]+=sz[v];
		mxdep[u]=max(mxdep[v]+1,mxdep[u]);
		for(int x:son[v])son[u].pb(x),gson[u][x]=1;
	}
	f[u][u].resize(mxdep[u]+1);
	for(int v:son[u]){
		if(v==u)continue;
		if(v==ls[u]||v==rs[u]){f[u][v].resize(mxdep[u]+1);continue;}
		int len=0;
		if(gson[u][v]==0)Max(len,mxdep[rs[u]]+1);
		else Max(len,mxdep[ls[u]]+1);
		f[u][v].resize(len+1);
	}
	if(u==1){
		if(ls[u]&&rs[u]){
			solve1();
			swap(ls[u],rs[u]);
			solve1();
		}else{
			for(int v1:son[ls[u]])for(int i=0;i<f[ls[u]][v1].sz;++i)
				Min(ans,f[ls[u]][v1][i]+1ll*c[u]*(i+1));
		}
		return;
	}
	if(ls[u]&&rs[u]){
		solve(u);
		swap(ls[u],rs[u]);
		solve(u);
	}else{
		for(int v1:son[ls[u]])for(int i=0;i<f[ls[u]][v1].sz;++i)
			Min(f[u][u][i+1],f[ls[u]][v1][i]+c[u]),
			Min(f[u][v1][0],f[ls[u]][v1][i]+1ll*c[u]*(i+1)+c[v1]);
	}
}
int n;
int main(){
	n=read();
	for(int i=1;i<=n;++i)c[i]=read();
	for(int i=2,fa;i<=n;++i){
		fa=read();
		if(!ls[fa])ls[fa]=i;
		else rs[fa]=i;
	}
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}

遊記參見另一篇部落格