题意简述
给定一个图,点数1e5,边数1e6。定义一个“半联通”的子图为:对于任意点 ( u , v ) (u,v) (u,v),满足: u u u能到 v v v,或 v v v能到 u u u。求最大的半联通子图点数,并求出满足像这样最大的解有多少个。
思路
(这个题是我做志愿者的时候做的。。。背着老师偷偷做题真tm爽。。。)
看起来这个“半联通”和“强连通”有点像,只不过一个是且,一个是或。但就是因为这个且改成或,就不好做了。
但是我们想一想,一个强连通分量是不是一个半联通子图?(woc我个智障想了半天才想到这一点)是的!
所以我们把强连通分量缩点即珂。由于要求最大点数,所以缩点时要维护这个分量里的点数。
然后图就变成了一个
D
A
G
DAG
DAG。在
D
A
G
DAG
DAG里就好做很多了。而且有一个明显的结论:
D
A
G
DAG
DAG中的半联通子图一定是一个链。
如果你不知道为啥,看这里的解释
此时显然我们选择了两条链。但是这两个红色的点就无法到达了,两边都不能到达,所以连“半联通”也不算。所以我们只能选择一条链。
然后就变成了带权的最长链问题了。
d
p
dp
dp即珂。
然后求个数?套路的,我们都
d
p
dp
dp了,在能取到相同的
d
p
dp
dp值时,用
+
=
+=
+=记录个数即珂。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 100100
#define MEM(x,a) memset(x,a,sizeof(x))
#define CLS(x) memset(x,0,sizeof(x))
class Graph
{
public:
int *head;
int EdgeCount;
struct Edge
{
int To,Label,Next;
}*Ed;
int MAX;
void SET(int len)
{
MAX=(len<<1)+100;
Ed=new Edge[MAX];
head=new int[MAX];
memset(head,-1,sizeof(int)*MAX);
memset(Ed,-1,sizeof(Edge)*MAX);
EdgeCount=0;
}
void clear()
{
memset(head,-1,sizeof(int)*MAX);
memset(Ed,-1,sizeof(Edge)*MAX);
EdgeCount=0;
}
void AddEdge(int u,int v,int w)
{
++EdgeCount;
Ed[EdgeCount]=(Edge){v,w,head[u]};
head[u]=EdgeCount;
}
void Add2(int u,int v,int w)
{
AddEdge(u,v,w);AddEdge(v,u,w);
}
int Start(int u)
{
return head[u];
}
int To(int u)
{
return Ed[u].To;
}
int Label(int u)
{
return Ed[u].Label;
}
int Next(int u)
{
return Ed[u].Next;
}
}G;
struct Edge
{
int u,v,w;
}E[1001000];
bool operator<(Edge b,Edge a)
{
return make_pair(a.u,a.v)<make_pair(b.u,b.v);
}
bool operator==(Edge a,Edge b){return !(a<b) and !(b<a);}
int ecnt=0;void AddEd(int u,int v,int w){E[++ecnt]=(Edge){u,v,w};}
int n,m,x;
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n),R1(m),R1(x);
G.SET(1000000);
CLS(E);
for(int i=1;i<=m;++i)
{
int u,v;
R1(u),R1(v);
G.AddEdge(u,v,1);
AddEd(u,v,1);
}
}
//normal
int DFSid[N],low[N],time=0;
bool In[N];stack<int>STK;
int SCCid[N],SCCcnt;
//addition
int size[N];
void Tarjan(int u)
{
DFSid[u]=low[u]=++time;
In[u]=1;STK.push(u);
for(int i=G.Start(u);~i;i=G.Next(i))
{
int v=G.To(i);
if (!DFSid[v])
{
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (In[v])
{
low[u]=min(low[u],low[v]);
}
}
if (DFSid[u]==low[u])
{
++SCCcnt;
int top;
do
{
top=STK.top();STK.pop();
In[top]=0;
SCCid[top]=SCCcnt;
++size[SCCcnt];
} while (top!=u);
}
}
int dp[N][2];
//dp[i][0]:max length
//dp[i][1]:how many dp[i][0] exists
queue<int>Q,EmptyQ;
int indeg[N];
void Topo_DP()
{
Q=EmptyQ;
for(int i=1;i<=SCCcnt;++i)
{
if (!indeg[i])
{
Q.push(i);
dp[i][0]=size[i];
dp[i][1]=1;
}
}
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=G.Start(u);~i;i=G.Next(i))
{
int v=G.To(i);
--indeg[v];
if (indeg[v]==0)
{
Q.push(v);
}
if (dp[u][0]+size[v]==dp[v][0])
{
dp[v][1]+=dp[u][1];
dp[v][1]%=x;
}
else if (dp[u][0]+size[v]>dp[v][0])
{
dp[v][1]=dp[u][1];
dp[v][0]=dp[u][0]+size[v];
}
}
}
int pos=0;
int cnt=0;
for(int i=1;i<=SCCcnt;++i)
{
if (dp[i][0]>dp[pos][0]) pos=i,cnt=dp[i][1];
else if (dp[i][0]==dp[pos][0]) cnt+=dp[i][1],cnt%=x;
}
printf("%d\n%d\n",dp[pos][0],cnt);
}
void Soviet()
{
for(int i=1;i<=n;++i)
{
if (!DFSid[i]) Tarjan(i);
}
CLS(E);ecnt=0;
for(int i=1;i<=n;++i)
{
for(int j=G.Start(i);~j;j=G.Next(j))
{
int v=G.To(j);
if (SCCid[i]!=SCCid[v])
{
AddEd(SCCid[i],SCCid[v],1);
}
}
}
sort(E+1,E+ecnt+1);
G.clear();
for(int i=1;i<=ecnt;++i)
{
if (!(E[i]==E[i-1]))
{
G.AddEdge(E[i].u,E[i].v,1);
++indeg[E[i].v];
}
}
Topo_DP();
}
void IsMyWife()
{
if (0)
{
freopen("download_testdatas\\semi3.in","r",stdin);
// freopen("","w",stdout);
}
Input();
Soviet();
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}

该博客介绍了如何解决最大半连通子图问题,通过分析题意,将问题转化为寻找强连通分量并进行缩点。将原图转换为DAG后,问题简化为寻找最长链。利用动态规划方法求解,并在求解过程中记录相同解的数量。

1491

被折叠的 条评论
为什么被折叠?



