20240724【省選】模擬

和蜀玩發表於2024-07-24

掛了四分,掉了一名,不過這也說明我的實力就只有這點,根本不夠,果然以後還是直接【資料刪除】得了。

T1

其實就是個樹剖,每個點維護左右子樹的最大深度以及左右子樹內的最大答案,然後就…………沒了?

淦,也是實現問題,應該想到的。然後就是修改邊權是改成 \(w-a_p\)\(a_i\) 是記錄下來的 \(i\) 的父邊邊權。

真沒了

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define ll int
#define ls now<<1
#define rs now<<1|1
const ll N=2*114514,M=1919810;
struct xx{
	ll next,to;
}e[2*N];
ll head[2*N],cnt;
void add(ll x,ll y){
	e[++cnt].next=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
ll n,m,fr[N];
ll in[N],out[N],t_cnt;
void dfs(ll u,ll fa){
	in[u]=out[u]=++t_cnt;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		out[u]=++t_cnt;
	}
}
struct tree{
	ll res,tag;
	ll mx,mn,lmx,rmx;
}t[4*N];
void modify(ll now,ll k){
	t[now].mx+=k,t[now].mn+=k;
	t[now].lmx-=k,t[now].rmx-=k;
	t[now].tag+=k;
}
void pushup(ll now){
	t[now].mx=max(t[ls].mx,t[rs].mx),t[now].mn=min(t[ls].mn,t[rs].mn);
	t[now].lmx=max(max(t[ls].lmx,t[rs].lmx),t[ls].mx-2*t[rs].mn);
	t[now].rmx=max(max(t[ls].rmx,t[rs].rmx),t[rs].mx-2*t[ls].mn);
	t[now].res=max(t[ls].res,t[rs].res);
	t[now].res=max(t[now].res,max(t[ls].mx+t[rs].rmx,t[rs].mx+t[ls].lmx));
}
void pushdown(ll now){
	ll k=t[now].tag;
	if(!k) return;
	modify(ls,k),modify(rs,k);
	t[now].tag=0;
}
void build(ll now,ll l,ll r){
	if(l==r){
		t[now].lmx=t[now].rmx=t[now].res=-1;
		return;
	}
	ll mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	pushup(now);
}
void update(ll now,ll l,ll r,ll x,ll y,ll k){
	if(l>=x&&r<=y){
		modify(now,k);
		return;
	}
	pushdown(now);
	ll mid=(l+r)>>1;
	if(x<=mid) update(ls,l,mid,x,y,k);
	if(y>mid) update(rs,mid+1,r,x,y,k);
	pushup(now);
}
ll p,w,a[N];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=2;i<=n;++i){
		cin>>fr[i];
		add(i,fr[i]),add(fr[i],i);
	}
	build(1,1,n);
	dfs(1,0);
	for(int i=1;i<=m;++i){
		cin>>p>>w;
		update(1,1,t_cnt,in[p],out[p],w-a[p]);
		a[p]=w;
		cout<<t[1].res<<'\n';
	}
	return 0;
}/*5 4
1 1 2 2
4 8
4 3
2 2
5 7
擁有蒟蒻的力量,退役是必然的*/

T2

知道是個 dp,狀態設出來了,推不出來,太菜了

\(dp[u][i][j][k]\) 表示在 \(u\) 的子樹中選 \(u\) 並總共選了 \(i\) 個🐂,\(j\) 個🐏,\(k\) 個小C時的方案數。因為 \(A+B+C\le \frac{n}{2}\),所以對於 \(n\le 50\) 的資料直接 \(O(n(ABC)^2)\) 暴力 dp 就行了。

正解考慮樹形依賴揹包,透過我和想到的非常接近的 dp 可以做到 \(O(nABC)\),但是這樣只能算包含根結點的連通塊數,於是考慮套個點分治解決,複雜度 \(O(nABC\cdot\log n)\),點分治是我沒想到的/ng

需要注意的是,dp 陣列直接開 dp[201][201][201][201] 是要爆的,但是 \(A+B+C\le\frac{n}{2}\),所以可以考慮把後三維壓縮成一維,能存下。

不過為啥嚴隊程式碼是最裂解啊

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define ll int
const ll N=205,M=55,mod=998244353;
ll cf[N][N];
struct xx{
	ll next,to;
}e[2*N];
ll head[2*N],cnt;
void add(ll x,ll y){
	e[++cnt].next=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
ll n,A,B,C,col[N],siz[N],ans;
ll dp[N][43999],vis[N],mx,rt,mxz; //米學長(霧 
void findrt(ll u,ll fa){
	siz[u]=1;
	ll maxn=0;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(v==fa||vis[v]) continue;
		findrt(v,u);
		siz[u]+=siz[v];
		maxn=max(maxn,siz[v]);
	}
	maxn=max(maxn,mxz-siz[u]);
	if(mx>maxn) mx=maxn,rt=u;
}
ll dfn[N],t_cnt,c[N],sz[N];
void dfs(ll u,ll fa){
	dfn[u]=++t_cnt;
	c[t_cnt]=col[u];
	sz[dfn[u]]=1;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(v==fa||vis[v]) continue;
		dfs(v,u);
		sz[dfn[u]]+=sz[dfn[v]];
	}
}
ll &f(ll u,ll i,ll j,ll k){
	return dp[u][i*(B+1)*(C+1)+j*(C+1)+k];
}
void solve(ll u){
	memset(dp,0,sizeof(dp));
	t_cnt=0,dfs(u,0);
	f(1,0,0,0)=1;
	for(int i=1;i<=t_cnt;++i)
		for(int x=0;x<=A;++x)
			for(int y=0;y<=B;++y)
				for(int z=0;z<=C;++z){
					ll val=f(i,x,y,z);
					if(!val) continue;
					if(c[i]==0&&x<A) (f(i+1,x+1,y,z)+=val)%=mod;
					if(c[i]==1&&y<B) (f(i+1,x,y+1,z)+=val)%=mod;
					if(c[i]==2&&z<C) (f(i+1,x,y,z+1)+=val)%=mod;
					(f(i+sz[i],x,y,z)+=val)%=mod;
				}
	for(int x=0;x<=A;++x)
		for(int y=0;y<=B;++y)
			for(int z=0;z<=C;++z)
				(ans+=f(t_cnt+1,x,y,z))%=mod;
	--ans;
}
void calc(ll u){
	vis[u]=1;
	solve(u);
	ll xxs=mxz;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(vis[v]) continue;
		mxz=siz[u]>siz[v]?siz[v]:xxs-siz[u];
		mx=1919810;
		findrt(v,u),calc(rt); 
	}
}
int main(){
	//freopen("b.in","r",stdin);
	//freopen("b.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>A>>B>>C;
	for(int i=1;i<=n;++i) cin>>col[i];
	for(int i=1;i<n;++i){
		ll a,b;
		cin>>a>>b;
		add(a,b),add(b,a);
	}
	mxz=n,mx=1919810;
	findrt(1,0),calc(rt);
	cout<<ans;
	return 0;
}/*5 1 1 1
0 1 0 0 2
1 2
1 3
3 4
4 5
擁有蒟蒻的力量,退役是必然的*/

T3

xrq 場切,○| ̄|_ 不過講過我也做不來,賽時只有大眾分/ll

相關文章