20240724【省选】模拟
挂了四分,掉了一名,不过这也说明我的实力就只有这点,根本不够,果然以后还是直接【数据删除】得了。
T1
其实就是个树剖,每个点维护左右子树的最大深度以及左右子树内的最大答案,然后就…………没了?
淦,也是实现问题,应该想到的。然后就是修改边权是改成 , 是记录下来的 的父边边权。
真没了
点击查看代码
#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,状态设出来了,推不出来,太菜了
设 表示在 的子树中选 并总共选了 个🐂, 个🐏, 个小C时的方案数。因为 ,所以对于 的数据直接 暴力 dp 就行了。
正解考虑树形依赖背包,通过我和想到的非常接近的 dp 可以做到 ,但是这样只能算包含根结点的连通块数,于是考虑套个点分治解决,复杂度 ,点分治是我没想到的/ng
需要注意的是,dp 数组直接开 dp[201][201][201][201]
是要爆的,但是 ,所以可以考虑把后三维压缩成一维,能存下。
不过为啥严队代码是最裂解啊
点击查看代码
#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
頑張って