posts - 127,  comments - 4,  trackbacks - 0

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

按照题意来说很明显的是如果是一个环的话它肯定符合条件,而且为了满足最大的条件,环里的点一定是一起取的,所以先用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\n%lld\n",ans,ans2); return 0; }

 

posted on 2017-12-14 16:23  nhc2014  阅读(...)  评论(...编辑  收藏