• bzoj1093[ZJOI2007]最大半连通子图(dp+tarjan)


    因为我数组开错了调了很久。。

    按照题意来说很明显的是如果是一个环的话它肯定符合条件,而且为了满足最大的条件,环里的点一定是一起取的,所以先用tarjan缩点。

    缩点后实质上就是取一条最长链。

    然后很明显就可以按照拓扑序来dp了,side[i]表示到当前这个点为止,链的最长的长度是多少?dp[i]表示如果取最长链的话有几种可能性。

    然后转移就是,

    if (side[i]==side[j]+size[i]) dp[i]+=dp[j];

    else if (side[i]<side[j]+size[i]) dp[i]=dp[j];

    size[i]表示i这个点在未缩点之前有多少个点,j到i有一条边。

    还有一个点就是,我们取的半联通子图是点集,所以要去掉重边,才能让上述dp有效。

    #include<iostream>
    #include<vector>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    #include<stack>
    #include<cstdlib>
    #define maxn 200009
    #define maxm 2000009
    using namespace std;
    typedef long long ll;
    struct ding2{
      int lef,righ;
    }bia[maxm];
    struct ding{
      int to,next;
    }edge[maxm];
    queue<int>q;
    int num,n,m,ans;
    int head[maxn],ru[maxn],side[maxn],size[maxn];
    ll mo,dp[maxn],ans2;
    int cnt;
    void add(int u,int v)
    {
      edge[++cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;
    }
    void tuopu()
    {
      while (!q.empty())
      {
          int now=q.front();q.pop();
          ans=max(ans,side[now]);
          for (int i=head[now];i;i=edge[i].next)
          {
            int y=edge[i].to; ru[y]--;
            if (side[y]<size[y]+side[now])
            {
            side[y]=size[y]+side[now];
            dp[y]=dp[now]; 
          }
          else if (side[y]==size[y]+side[now])
          {
           dp[y]=(dp[y]+dp[now])%mo;
          }
            if (ru[y]==0) q.push(y);
        }
      }
    }
    
    stack<int>s;
    int low[maxn],nod[maxn],dfn[maxn],p=0;
    vector<int>g[maxn];
    bool in[maxn];
    void tarjan(int x)
    {
      dfn[x]=low[x]=++p;
      s.push(x);in[x]=true;
      for (int i=0;i<g[x].size();i++)
      {
          int y=g[x][i];
          if (!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
          else if (in[y]) low[x]=min(low[x],dfn[y]);
      }
      if (low[x]==dfn[x])
      {
           ++num; 
           while (s.top()!=x)
           {
             nod[s.top()]=num; size[num]++;
           in[s.top()]=false;s.pop();     
         }
         nod[s.top()]=num;size[num]++;in[s.top()]=false;s.pop();
      }
    }
    bool cmp(ding2 t1,ding2 t2)
    {
      return (t1.lef==t2.lef?t1.righ<t2.righ:t1.lef<t2.lef);
    }
    int main()
    {
      //freopen("semi5.in","r",stdin);
      //freopen("haha.out","w",stdout);
      scanf("%d%d%lld",&n,&m,&mo);
      int x,y;
      for (int i=1;i<=m;i++)
      {
          scanf("%d%d",&x,&y);
          bia[i].lef=x;bia[i].righ=y;
          g[x].push_back(y);
      }
      num=0;
    //缩点
    for (int i=1;i<=n;i++) if (dfn[i]==0) tarjan(i); for (int i=1;i<=m;i++) { bia[i].lef=nod[bia[i].lef]; bia[i].righ=nod[bia[i].righ]; } sort(bia+1,bia+1+m,cmp);
    //去重边
    int lastx=0,lasty=0; for (int i=1;i<=m;i++) { int p1=bia[i].lef,p2=bia[i].righ; if ((p1!=p2)&& (!((p1==lastx)&&(p2==lasty)))) {add(p1,p2); ru[p2]++;} lastx=p1;lasty=p2; } for (int i=1;i<=num;i++) if (ru[i]==0) q.push(i),side[i]=size[i],dp[i]=1;
    //dp tuopu();
    for (int i=1;i<=num;i++) if (side[i]==ans) ans2=(ans2+dp[i])%mo;
    //枚举哪些点是链的终点来统计答案 printf(
    "%d %lld ",ans,ans2); return 0; }
  • 相关阅读:
    span设置宽和高当没有内容的时候也可撑开
    span设置宽和高当没有内容的时候也可以撑开
    块级元素以及内联元素大总结
    内存泄露问题
    Sqlcompletefree
    运用SET ANSI_PADDING OFF创建某个字段为自增列的表,以及插入数据
    Sql Server中通配符
    sql server 2008查询窗口怎么显示行数
    sql server 2008语句中的go有什么用?
    SQL Server 2008 R2[ALTER]列属性修改
  • 原文地址:https://www.cnblogs.com/2014nhc/p/8038376.html
Copyright © 2020-2023  润新知