[HNOI/AHOI2018]毒瘤

[Luogu4426] [LOJ2496]

题解

#include<cstdio>
#include<cstring>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

const int MAXN=1e5+5;
const int MAXM=2e5+25;
const int MAXX=15;
const int mod=998244353;

int f[MAXN][2],p[MAXN][2];bool b[MAXN][2];
int dfn[MAXN],size[MAXN],eu[MAXN],ev[MAXN];
bool mark[MAXN],vis[MAXN];
int n,m,dft,Xcnt,ans;

inline int add(int x,int y){x+=y;return x>mod?x-mod:x;}
inline int mul(LL x,int y){x*=y;return x>mod?x%mod:x;}

struct Node{
    int x,y;
    inline Node(){}
    inline Node(int _x,int _y){x=_x,y=_y;}
    inline friend Node operator + (Node a,Node b){return Node(add(a.x,b.x),add(a.y,b.y));}
    inline friend Node operator * (Node a,int b){return Node(mul(a.x,b),mul(a.y,b));}
}k[MAXN][2];

struct Edge{int v,next;}e[MAXM];
int first[MAXN],Ecnt=0;
inline void Add_edge(int u,int v){
    e[++Ecnt]=(Edge){v,first[u]};
    first[u]=Ecnt;
}

struct EXedge{int v,next;Node a,b;}E[MAXM];
int First[MAXN],ECNT=0;
inline void ADD_EDGE(int u,int v,Node a,Node b){
    E[++ECNT]=(EXedge){v,First[u],a,b};
    First[u]=ECNT;
}

inline void dfs1(int u,int pre){
    dfn[u]=++dft;
    for(int i=first[u],v=e[i].v;i;v=e[i=e[i].next].v){
        if(v==pre) continue;
        if(!dfn[v]){
            dfs1(v,u);
            size[u]+=size[v];
        }
        /*else if(dfn[u]<dfn[v]){
            eu[++Xcnt]=u,ev[Xcnt]=v;
            mark[u]=mark[v]=1;
            //printf("%d-%d\n",u,v);
        }*/
        else{
            mark[u]=1;//mark[u]是必须即时更新的 
            if(dfn[u]<dfn[v]) eu[++Xcnt]=u,ev[Xcnt]=v;
        }
    }
    mark[u]|=size[u]>=2;
    size[u]=size[u]||mark[u];
}

inline int dfs2(int u){
    vis[u]=true;
    p[u][0]=p[u][1]=1;
    int pos=0,w;
    for(int i=first[u];i;i=e[i].next){
        int v=e[i].v;
        if(vis[v]) continue;//这样和dfs1配合起来可以保证对于非树边的操作正确,在本题中非树边就直接单独讨论了,和dfs2无关
        w=dfs2(v);
        if(!w){
            p[u][1]=1ll*p[u][1]*p[v][0]%mod;//没有关键点:直接按普通的更新 
            p[u][0]=1ll*p[u][0]*(p[v][0]+p[v][1])%mod;
        }
        else if(mark[u]) ADD_EDGE(u,w,k[v][0]+k[v][1],k[v][0]);
        else k[u][1]=k[v][0],k[u][0]=k[v][0]+k[v][1],pos=w;//k已经从下面转移过来了
                                         //记得更新pos(最近的关键点)!!!
    }
    if(mark[u]) k[u][0]={1,0},k[u][1]={0,1},pos=u;//类似于初始化的操作
    else k[u][0]=k[u][0]*p[u][0],k[u][1]=k[u][1]*p[u][1];//然后再乘自己这个点,定义为处理完这个点的子树的系数
    return pos;
}

inline void dp(int u){
    //f[u][0]=b[u][0]*p[u][0];//这样写也是错的 
    //f[u][1]=b[u][1]*p[u][1];
    f[u][0]=(b[u][1]^1)*p[u][0];//强制选了另一边,这一边就不能选 
    f[u][1]=(b[u][0]^1)*p[u][1];
    for(int i=First[u];i;i=E[i].next){
        int v=E[i].v;
        dp(v);
        f[u][0]=mul(f[u][0],add(mul(E[i].a.x,f[v][0]),mul(E[i].a.y,f[v][1])));//用处理好了的系数直接相乘 
        f[u][1]=mul(f[u][1],add(mul(E[i].b.x,f[v][0]),mul(E[i].b.y,f[v][1])));
    }
}

int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        Add_edge(x,y);
        Add_edge(y,x);
    }
    dfs1(1,0);mark[1]=1;//根节点作为很多对的lca,必须标记 
    dfs2(1);
    for(int i=0;i<=((1<<Xcnt)-1);i++){
        for(int j=1;j<=Xcnt;j++){
            if(i&(1<<(j-1))) b[eu[j]][1]=b[ev[j]][0]=1;// (1,0) 或者 (0,_) 
            else b[eu[j]][0]=1;
        }
        dp(1);
        ans=add(ans,add(f[1][0],f[1][1]));
        for(int j=1;j<=Xcnt;j++){
            if(i&(1<<(j-1))) b[eu[j]][1]=b[ev[j]][0]=0;
            else b[eu[j]][0]=0;
        }
    }
    printf("%d\n",ans);
}

转载于:https://www.cnblogs.com/lizehon/p/10567892.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值