BZOJ 1093: [ZJOI2007]最大半连通子图( tarjan + dp )

WA了好多次...

先tarjan缩点, 然后题意就是求DAG上的一条最长链. dp(u) = max{dp(v)} + totu, edge(u,v)存在. totu是scc(u)的结点数. 其实就是记忆化搜一下...重边就用set判一下

-------------------------------------------------------------------------------------------

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<iostream>
#include<cctype>
#include<set>
 
using namespace std;
 
const int maxn = 100009;
const int INF = 0x3F3F3F3F;
 
inline int read() {
char c = getchar();
int ret = 0;
for(; !isdigit(c); c = getchar());
for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
return ret;
}
 
struct edge {
int to;
edge* next;
} E[1000009], *pt = E, *head[maxn];
 
void addedge(int u, int v) {
pt->to = v; pt->next = head[u]; head[u] = pt++;
}
 
int N, scc[maxn], dfn[maxn], low[maxn], tot[maxn], CK = 0, n = -1, MOD;
stack<int> S;
set<pair<int, int> > bst;
 
void tarjan(int x) {
dfn[x] = low[x] = ++CK;
S.push(x);
for(edge* e = head[x]; e; e = e->next)
if(!dfn[e->to])
tarjan(e->to), low[x] = min(low[e->to], low[x]);
else if(!~scc[e->to])
low[x] = min(low[x], dfn[e->to]);
if(dfn[x] == low[x]) {
int t; n++;
do {
t = S.top(); S.pop();
tot[n]++;
scc[t] = n;
} while(t != x);
}
}
 
namespace DP {
edge E[1000009], *pt = E, *head[maxn];
int dp[maxn], cnt[maxn];
bool vis[maxn];
void addedge(int u, int v) {
pt->to = v; pt->next = head[u]; head[u] = pt++;
}
void init() {
memset(dp, 0, sizeof dp);
memset(cnt, 0, sizeof cnt);
memset(vis, 0, sizeof vis);
}
void Dp(int x) {
if(vis[x]) return;
vis[x] = true;
for(edge* e = head[x]; e; e = e->next) {
Dp(e->to);
if(dp[e->to] > dp[x])
dp[x] = dp[e->to], cnt[x] = cnt[e->to];
else if(dp[e->to] == dp[x] && (cnt[x] += cnt[e->to]) >= MOD) 
cnt[x] -= MOD;
}
dp[x] += tot[x];
if(!cnt[x]) cnt[x] = 1;
}
void work() {
for(int i = 0; i <= n; i++) 
if(!vis[i]) Dp(i);
int ans = 0, ans0 = 0;
for(int i = 0; i <= n; i++)
if(dp[i] > ans) ans = dp[i], ans0 = cnt[i];
else if(ans == dp[i] && (ans0 += cnt[i]) >= MOD) ans0 -= MOD;
printf("%d %d ", ans, ans0);
}
}
 
void AddEdge() {
for(int x = 0; x < N; x++)
for(edge* e = head[x]; e; e = e->next) 
if(scc[x] != scc[e->to] && bst.find(make_pair(scc[x], scc[e->to])) == bst.end()) {
DP::addedge(scc[x], scc[e->to]);
bst.insert(make_pair(scc[x], scc[e->to]));
}
}
 
void TARJAN() {
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(scc, -1, sizeof scc);
memset(tot, 0, sizeof tot);
for(int i = 0; i < N; i++) if(!dfn[i]) tarjan(i);
}
 
void init() {
N = read();
int m = read();
MOD = read();
while(m--) {
int u = read() - 1, v = read() - 1;
addedge(u, v);
}
DP::init();
}
 
int main() {
init();
TARJAN();
DP::init();
AddEdge();
DP::work();
return 0;
}

-------------------------------------------------------------------------------------------

1093: [ZJOI2007]最大半连通子图

Time Limit: 30 Sec  Memory Limit: 162 MB
Submit: 2105  Solved: 841
[Submit][Status][Discuss]

Description

Input

第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述。接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。

Output

应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.

Sample Input

6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4

Sample Output

3
3

HINT

对于100%的数据, N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8。

Source

原文地址:https://www.cnblogs.com/JSZX11556/p/4875587.html