【考試總結】2022-05-10
簡單題
使用能維護這個區間歷史被加了多少的線段樹即可,在每個 \(a_l=a_{l+1}=\dots=a_r\) 的線段上對 \(b\) 進行加法即可
Code Display
const int N=5e5+10; int len[N<<2],tag[N<<2],cov[N<<2],b[N],n,m,Q; signed main(){ freopen("easy.in","r",stdin); freopen("easy.out","w",stdout); n=read(); m=read(); Q=read(); #define ls p<<1 #define rs p<<1|1 #define lson p<<1,l,mid #define rson p<<1|1,mid+1,r auto push_add=[&](const int p,const int v){ if(~cov[p]) b[cov[p]]+=len[p]*v; else tag[p]+=v; }; auto push_down=[&](const int p){ if(tag[p]){ push_add(ls,tag[p]); push_add(rs,tag[p]); tag[p]=0; } if(~cov[p]){ cov[ls]=cov[p]; cov[rs]=cov[p]; cov[p]=-1; } }; function<void(int,int,int)>build=[&](const int p,const int l,const int r){ len[p]=r-l+1; cov[p]=-1; if(l==r) return cov[p]=read(),void(); int mid=(l+r)>>1; build(lson); build(rson); }; build(1,1,n); while(Q--){ int opt=read(),st=read(),ed=read(),v=read(); function<void(int,int,int)> Plus=[&](int p,int l,int r){ if(st<=l&&r<=ed) return push_add(p,v); int mid=(l+r)>>1; push_down(p); if(st<=mid) Plus(lson); if(ed>mid) Plus(rson); return ; }; function<void(int,int,int)> Cover=[&](int p,int l,int r){ if(st<=l&&r<=ed) return cov[p]=v,void(); int mid=(l+r)>>1; push_down(p); if(st<=mid) Cover(lson); if(ed>mid) Cover(rson); return ; }; if(opt==1) Cover(1,1,n); else Plus(1,1,n); } function<void(int,int,int)>dfs=[&](const int p,const int l,const int r){ if(l==r) return ; int mid=(l+r)>>1; push_down(p); dfs(lson); dfs(rson); return ; }; dfs(1,1,n); rep(i,1,m) print(b[i]); #undef ls #undef rs #undef lson #undef rson return 0; }
樹上游走
設 \(f_{x}\) 表示第一次經過 \(x\) 且 \(x\) 的子孫都沒有經過的,父親也已經消失的情況下得到的期望權值,\(g_x\) 表示同樣狀況下第二次經過 \(x\) 得到的期望權值
設 \(h_{x}\) 表示第一次經過 \(x\) 且 \(x\) 的父親依然存在,不經過 \(x\) 的父親得到的期望答案,而 \(p_x\) 表示不返回父親的概率
\(g_x\) 的轉移是平凡的,列舉走到了哪個兒子使用 \(f_{son}\) 轉移即可
剩下三種轉移考慮走到兒子之後的處境:
-
走到子樹裡面不再回頭,分別使用 \(h_{son},p_{son}\) 來轉移即可
-
走到兒子節點立刻返回,此時仍然可以走到任何一個兒子,不過先前走的再走就得用 \(g_{son}\)
-
走到兒子子樹的另一節點再返回,區別在於返回之後兒子不存在了,概率上的處理要減掉上一個情況發生的 \(\frac{1}{deg_{son}}\) 以及不再返回的概率
而這個情況在只有一個出邊的時候的 \(f\) 轉移需要用到自己的權值
邊界就是 \(f_x=g_x=a_x,p_x=h_x=0\)
Code Display
const int N=1e5+10; int inv[N],f[N],g[N],h[N],p[N]; int n,S,a[N]; vector<int> G[N]; signed main(){ freopen("walk.in","r",stdin); freopen("walk.out","w",stdout); n=read(); S=read(); for(int i=inv[1]=inv[0]=1;i<=n;++i) a[i]=read(); rep(i,2,n){ inv[i]=mod-mul(mod/i,inv[mod%i]); int u=read(),v=read(); G[u].emplace_back(v); G[v].emplace_back(u); } function<void(int,int)>solve=[&](int x,int fat){ int deg=0,sumf=0; for(auto t:G[x]) if(t!=fat){ deg++; solve(t,x); ckadd(sumf,f[t]); } if(!deg){ f[x]=g[x]=a[x]; return ; } for(auto t:G[x]) if(t!=fat){ ckadd(f[x],h[t]); int dt=G[t].size()-1; int down=del(1,add(p[t],inv[dt+1])),oth=del(sumf,f[t]); ckadd(f[x],mul(mul(inv[deg],inv[dt+1]),add(g[t],oth))); if(deg==1){ ckadd(f[x],mul(a[x],down)); }else{ ckadd(f[x],mul(down,mul(inv[deg-1],oth))); } ckadd(g[x],f[t]); ckadd(h[x],h[t]); ckadd(p[x],p[t]); ckadd(h[x],mul(mul(inv[dt+1],inv[deg+1]),add(g[t],oth))); ckadd(p[x],mul(mul(inv[dt+1],inv[deg+1]),deg)); ckadd(h[x],mul(down,mul(inv[deg],oth))); ckadd(p[x],mul(down,mul(inv[deg],deg-1))); } ckmul(f[x],inv[deg]); ckmul(g[x],inv[deg]); ckmul(p[x],inv[deg+1]); ckmul(h[x],inv[deg+1]); return ; }; solve(S,0); print(f[S]); return 0; }
樹的同構
列舉每個點作為根作為 \(S\) 點集中深度最小的點,再列舉此時有根樹上的另外一個點作為點集 \(T\) 中深度最小的點
設 \(f_{x,y}\) 表示 \(x\) 子樹和 \(y\) 子樹中包含根的聯通塊同構最多能在聯通塊中包含幾個點
轉移考慮計算出來所有 \(f_{u,v}[u\in son_x,v\in son_y]\),那麼可以進行一個二分圖最大帶權匹配即可,使用費用流時複雜度 \(\Theta(poly(n))\)
Code Display
const int N=500,inf=0x3f3f3f3f3f3f3f3f;
struct Network_Flow{
int incf[N],pre[N],dst[N];
bool inq[N];
int S,T,ecnt=1,tot,head[N];
struct edge{int to,nxt,lim,cst;}e[N<<3];
inline bool spfa(){
for(int i=1;i<=tot;++i) incf[i]=0,dst[i]=-inf;
dst[S]=0; incf[S]=inf;
queue<int> q; q.push(S);
while(q.size()){
int fr=q.front(); q.pop(); inq[fr]=0;
for(int i=head[fr];i;i=e[i].nxt) if(e[i].lim){
int t=e[i].to;
if(dst[fr]+e[i].cst>dst[t]){
dst[t]=dst[fr]+e[i].cst;
incf[t]=min(incf[fr],e[i].lim);
pre[t]=i;
if(!inq[t]) inq[t]=1,q.push(t);
}
}
}
return dst[T]!=-inf;
}
inline void adde(int u,int v,int w,int c){
e[++ecnt]={v,head[u],w,c};
head[u]=ecnt;
}
inline void add(int u,int v,int w,int c){return adde(u,v,w,c),adde(v,u,0,-c);}
inline void clear(){
rep(i,1,tot) head[i]=0;
ecnt=1; tot=0;
}
inline int EK(){
int sum=0;
while(spfa()){
int x=T;
while(x^S){
e[pre[x]].lim-=incf[T]; e[pre[x]^1].lim+=incf[T];
x=e[pre[x]^1].to;
}
sum+=dst[T];
}
return sum;
}
}F;
int u[N],v[N],n,fa[N];
vector<int> G[N];
int dp[N][N],id[N];
signed main(){
freopen("isomorphism.in","r",stdin); freopen("isomorphism.out","w",stdout);
while(~scanf("%lld",&u[++n])) ++u[n];
for(int i=n/2+1;i<=n;++i) v[i-n/2]=u[i];
n=n/2+1;
for(int i=1;i<n;++i) G[u[i]].emplace_back(v[i]),G[v[i]].emplace_back(u[i]);
int ban,ans=0;
function<void(int,int)>get_fa=[&](const int x,const int fat){
fa[x]=fat;
for(auto t:G[x]) if(t!=fat) get_fa(t,x);
return ;
};
function<void(int,int)>DP=[&](const int x,const int y){
if(~dp[x][y]) return ;
if(x==ban){
dp[x][y]=0;
return ;
}
for(auto a:G[x]) if(a!=fa[x]&&a!=ban) for(auto b:G[y]) if(b!=fa[y]) DP(a,b);
F.clear();
F.S=++F.tot; F.T=++F.tot;
for(auto a:G[x]) if(a!=fa[x]&&a!=ban) F.add(F.S,id[a]=++F.tot,1,0);
for(auto a:G[y]) if(a!=fa[y]) F.add(id[a]=++F.tot,F.T,1,0);
for(auto a:G[x]) if(a!=fa[x]&&a!=ban){
for(auto b:G[y]) if(b!=fa[y]){
F.add(id[a],id[b],1,dp[a][b]);
}
}
dp[x][y]=F.EK()+1;
return ;
};
rep(i,1,n){
memset(dp,-1,sizeof(dp));
get_fa(i,0);
rep(j,1,n) ban=j,DP(i,j);
rep(j,1,n) ckmax(ans,dp[i][j]);
}
print(ans);
return 0;
}