NOIP提高组模拟赛加1

A. 哪一天她能重回我身边

学网络瘤学傻了,用费用瘤搞掉第一问就在想怎么搞方案,然后越想越偏。。。。。

不仅没有搞出来第二问,而且费用瘤的复杂度。。。。

总之就是挂的很惨。。。。

这题居然是个树形DP???

把背面的数向正面的数连边,翻一张卡相当于把边反向,我们要用最少次数让所有点入度小于等于1,并且求出方案数

显然对每个联通块可以分开考虑

如果一个联通块n>m那么无论如何都无法满足要求

那么我们只需要考虑n==mn1==m两种情况

n1==m这不是棵树吗

我们再看一眼目的“用最少次数让所有点入度小于等于1

在树上就有且只有一个点入度为0,我们令这个点为树根,然后换根DP就好了

n==m基环树,环上要么顺时针要么逆时针,其实只有两种情况,随便找个环上的边断开,分别以两边为根DFS一次即可

最小次数是所有联通块最小次数和,方案数是所有联通块方案数乘起来。

基环树那里有点小细节。。

code
#include<cstdio>
#include<cstring>

using namespace std;

const int mod=998244353;
const int maxn=200005;
const int inf=0x3f;
int n,head[maxn],tot;
struct edge{int net,to,val;}e[maxn<<1|1];
bool vis[maxn];
void add(int u,int v,int w){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
    e[tot].val=w;
}
void link(int u,int v){
    add(u,v,1);add(v,u,0);
}
int cnt1,cnt2;
void dfs(int x){
    vis[x]=1;++cnt1;
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;++cnt2;
        if(vis[v])continue;
        dfs(v);
    }
}
int jh;

void DFS(int x,int fa){
    bool flag=0;vis[x]=1;
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==x)jh=i;
        if(v==fa){
            if(flag)jh=i;
            else flag=1;
        }else{
            if(vis[v])jh=i;
            else DFS(v,x);
        }
    }
}

int mi,cnt;
void TD_1(int x,int fa,int s1,int s2){
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fa||i==s1||i==s2)continue;
        if(e[i].val==0)++mi;
        TD_1(v,x,s1,s2);
    }
}
int now;
void TD_2(int x,int fa){
    if(now<mi)mi=now,cnt=0;
    if(now==mi)++cnt;
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fa)continue;
        if(e[i].val==1){
            ++now;TD_2(v,x);--now;
        }else{
            --now;TD_2(v,x);++now;
        }
    }
}

int main(){
    int T;scanf("%d",&T);
    for(int ask=1;ask<=T;++ask){
        tot=0;for(int i=1;i<=n+n;++i)head[i]=0;
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            int u,v;scanf("%d%d",&u,&v);link(v,u);
        }
        bool flag=1;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n+n;++i)
            if(!vis[i]){
                cnt1=cnt2=0;
                dfs(i);
                cnt2/=2;
                if(cnt2>cnt1){flag=0;break;}
            }
        if(!flag)printf("-1 -1\n");
        else{
            long long ans2=1;int ans1=0;
            memset(vis,0,sizeof(vis));
            for(int i=1;i<=n+n;++i){
                if(head[i]==0||vis[i])continue;
                jh=0;DFS(i,i);
                if(jh){
                    int u=e[jh].to,v,hj;
                    if(jh%2)hj=jh+1;else hj=jh-1;
                    v=e[hj].to;
                    if(e[jh].val==0){u^=v;v^=u;u^=v;}
                    mi=0;TD_1(u,u,jh,hj);int r1=mi;
                    mi=1;TD_1(v,v,jh,hj);
                    if(r1==mi)ans2=ans2*2%mod;
                    ans1+=r1>mi?mi:r1;
                }else{
                    mi=0;TD_1(i,i,0,0);
                    now=mi;cnt=0;TD_2(i,i);
                    ans2=ans2*cnt%mod;
                    ans1=ans1+mi;
                }    
            }
            printf("%d %lld\n",ans1,ans2);
        }
    }
    return 0;
}

考场推柿子,乱搞半天激动地发现解出了S,赶快实现,,,然后浮点数例外??

仔细观察,大概就是搞了半天整出的柿子是0S=0,解个毛线。。。

首先知道ab,一个简单的树形DP

Si表示以i为根的子树所有权值的和,整棵树的根为1

DP中我们可以得到bi=bfaSiSi+S1,这个显然是反推的关键

我们可以得到

bibfa=S12Si

错误搞法0=0就不说了。。

正解考虑

i=2nbibi>fa=(n1)S12i=2nSi

然后我们再想想当初是怎么求b1的,你会发现b1=i=2nSi

那么i=2nbibi>fa=(n1)S12b1

这里可以解出S1,然后剩下的就非常简单了

code
#include<cstdio>
#include<cstring>

using namespace std;
#define int long long
const int maxn=100000;
int head[maxn],tot,n;
struct edge{int to,net;}e[maxn<<1|1];
void add(int u,int v){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
}

int re[maxn],pr[maxn],s[maxn],dep[maxn],dt[maxn];

void DFS(int x,int fa){
    if(x!=1)dt[x]=re[x]-re[fa];
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fa)continue;
        DFS(v,x);
    }
}
void DP(int x,int fa){
    pr[x]=s[x];
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fa)continue;
        DP(v,x);
        pr[x]-=s[v];
    }
}
void worka(){
    for(int i=1;i<=n;++i)pr[i]=0;
    for(int i=1;i<=n;++i)dt[i]=0;
    for(int i=1;i<=n;++i)s[i]=0;
    DFS(1,1);
    for(int i=2;i<=n;++i)s[1]+=dt[i];
    s[1]+=re[1]+re[1];
    s[1]/=(n-1);
    for(int i=2;i<=n;++i)s[i]=(s[1]-dt[i])/2;
    DP(1,1); 
}

void dfs(int x,int fa){
    s[x]+=re[x];
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fa)continue;
        dep[v]=dep[x]+1;dfs(v,x);
        s[x]+=s[v];
    }
}
void dp(int x,int fa){
    if(x!=1)pr[x]=pr[fa]-s[x]-s[x]+s[1];
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fa)continue;
        dp(v,x);
    }
}
void workb(){
    for(int i=1;i<=n;++i)s[i]=0;
    dep[1]=0;dfs(1,1);
    for(int i=1;i<=n;++i)pr[i]=0;
    for(int i=1;i<=n;++i)pr[1]+=dep[i]*re[i];
    dp(1,1);
}

signed main(){
    int T;scanf("%lld",&T);
    for(int ask=1;ask<=T;++ask){
        scanf("%lld",&n);
        for(int i=1;i<=n;++i)head[i]=0;tot=0;
        for(int i=1;i<n;++i){
            int u,v;scanf("%lld%lld",&u,&v);
            add(u,v);add(v,u);
        }
        int type;scanf("%lld",&type);
        for(int i=1;i<=n;++i)scanf("%lld",&re[i]);  
        if(type)worka();
        else workb();
        for(int i=1;i<=n;++i)printf("%lld ",pr[i]);printf("\n");
    }    
    return 0;
}

posted @   Chen_jr  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示