聯合省選2022題解
Day1
T1 前處理器 preprocessor
題意
模擬 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
其中 A
,B
,C
,...,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
題意
給定一棵樹,每個點的點權要麼是 \(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
題意
有 \(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
題意
有 \(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
題意
給定一個長為 \(2n\) 的合法括號串,每個左括號有一個權值 \(val_i\),你可以進行如下兩種操作:
- 交換形如
p(A)(B)q
的串中A
和B
之間的兩個括號,變換為p(A()B)q
(其中p
,q
為任意串,可以為空,但不一定分別為合法括號序列,A
和B
必須是合法非空括號序列,下同),它的代價為 \(x\) 乘(A)
中第一個左括號的權值加上 \(y\) 乘(B)
中第一個左括號的權值。 - 交換形如
pABq
的串中的A
和B
,變換為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 最大權獨立集問題
題意
給定一個二叉樹,點有點權,刪一條邊的代價是兩個端點的點權和,刪完之後交換兩個點的點權。你需要找到一個刪邊的順序,要把所有邊刪完,最小化代價之和。
資料範圍:\(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;
}
遊記參見另一篇部落格