Tarjan 杂题

P5676 [GZOI2017] 小z玩游戏

将有趣程度作为点来建图 相当于是看哪些游戏的 w[i]e[i] 在同一个强连通分量中

这样是 O(n2) 的 无法通过

考虑换一种建边方式

若当前兴奋值为 a,某个游戏有趣程度为 w,兴奋程度为 ewa 的倍数。

可以视为兴奋值先从 a 变成了它的倍数 w,再变成 e

那我们就可以对每个数向它的倍数建边,再对于每个游戏从 we 建边。

跑一遍 Tarjan,对于每个游戏判断 we 是否在同一强连通分量即可。

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
const int maxn = 1e5;
int read()
{
    int x = 0 , f = 1;
    char ch = getchar();
    while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
    while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
    return x * f;
}

int n , ee[N] , w[N] , ans;

int low[N] , dfn[N] , timer , in[N] , id[N] , tot , sta[N] , top;

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

void tarjan ( int u )
{
    dfn[u] = low[u] = ++ timer;
    sta[++top] = u , in[u] = 1;
    for ( auto v : e[u] )
    {
        if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
        else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
    }
    if ( low[u] == dfn[u] )
    {
        ++ tot;
        while ( top )
        {
            int x = sta[top--];
            id[x] = tot , in[x] = 0;
            if ( x == u ) break;
        }
    }
}

void init()
{
    for ( int i = 1 ; i <= maxn ; i ++ ) e[i].clear(); 
    ans = timer = top = tot = 0;
    memset ( dfn , 0 , sizeof dfn );
    memset ( id , 0 , sizeof id );
    memset ( in , 0 , sizeof in );
}



signed main ()
{
    ios::sync_with_stdio(false);
    cin.tie(0) , cout.tie(0);
    int T = read();
    while ( T -- )
    {
        init();
        for ( int i = 1 ; i <= maxn ; i ++ )
            for ( int j = 2 ; j * i <= maxn ; j ++ )
                add ( i , j * i );
        n = read();
        for ( int i = 1 ; i <= n ; i ++ ) w[i] = read();
        for ( int i = 1 ; i <= n ; i ++ ) ee[i] = read();
        for ( int i = 1 ; i <= n ; i ++ ) add ( w[i] , ee[i] );
        for ( int i = 1 ; i <= maxn ; i ++ ) if ( !dfn[i] ) tarjan(i);
        for ( int i = 1 ; i <= n ; i ++ ) if ( id[w[i]] == id[ee[i]] ) ++ ans;
        cout << ans << endl;
    }
    return 0;
}

P1653 [USACO04DEC] Cow Ski Area G

每一个合法节点和它相邻的合法四个节点连边 跑一边 tarjan 成环的相当于都能互相到达 所以我们统计出度和入度为 0 的点的最大值就可以知道有多少个点不能直接到达了(因为我们最终目标是消灭所有入度出度为 0 的点 而它们显然不能两两连边 而是要从低节点连向高节点才可以消灭 所以得解)

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int inf = 0x3f3f3f3f;
const int N = 500 * 500 + 5;
const int M = 500 + 5;
int read()
{
    int x = 0 , f = 1;
    char ch = getchar();
    while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
    while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
    return x * f;
}

int n , m , sumrd , sumcd , a[M][M] , rd[N] , cd[N];

int low[N] , dfn[N] , timer , in[N] , id[N] , tot , sta[N] , top;

vector<int> e[N];
inl void adde ( int u , int v ) { e[u].eb(v); }
inl int check ( int u , int v ) { return 1 <= u && u <= n && 1 <= v && v <= m; }
inl int pos ( int u , int v ) { return ( u - 1 ) * m + v; }
inl void add ( int u , int v , int p , int q )
{
    if ( !check ( p , q ) ) return;
    if ( a[u][v] >= a[p][q] ) adde ( pos ( u , v ) , pos ( p , q ) );
}

void tarjan ( int u )
{
    dfn[u] = low[u] = ++ timer;
    sta[++top] = u , in[u] = 1;
    for ( auto v : e[u] )
    {
        if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
        else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
    }
    if ( low[u] == dfn[u] )
    {
        ++ tot;
        while ( top )
        {
            int x = sta[top--];
            id[x] = tot , in[x] = 0;
            if ( x == u ) break;
        }
    }
}

signed main ()
{
    ios::sync_with_stdio(false);
    cin.tie(0) , cout.tie(0);
    m = read() , n = read();
    for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= m ; j ++ ) a[i][j] = read();
    for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= m ; j ++ ) add ( i , j , i , j + 1 ) , add ( i , j , i , j - 1 ) , add ( i , j , i + 1 , j ) , add ( i , j , i - 1 , j );
    for ( int i = 1 ; i <= n * m ; i ++ ) if ( !dfn[i] ) tarjan(i);
    if ( tot == 1 ) return cout << 0 << endl , 0;
    for ( int u = 1 ; u <= n * m ; u ++ ) for ( auto v : e[u] ) if ( id[u] != id[v] ) ++ cd[id[u]] , ++ rd[id[v]];
    for ( int i = 1 ; i <= tot ; i ++ ) 
        sumrd += ( rd[i] == 0 ) , sumcd += ( cd[i] == 0 );
    cout << max ( sumrd , sumcd ) << endl;
    return 0;
}

P2321 [HNOI2006] 潘多拉的宝盒

相当于是我们将合法的 由 i 可以升级到 j 的咒语机加边 然后跑 tarjan 并求出最长链(最长升级序列)即可

对于加边 我们可以同时让 ij1 节点开始起跑 每次让它们一起在末尾加 0 或者一起在末尾加 1 如果第一个点能输出而第二个点不能的话 显然 1 无法升级到 2

否则 如果搜完都没有找到这样的节点 那么我们可以将 12 连边 表示升级关系

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int inf = 0x3f3f3f3f;
const int N = 500 + 5;
const int M = 500 + 5;
int read()
{
    int x = 0 , f = 1;
    char ch = getchar();
    while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
    while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
    return x * f;
}

int s , n , m , vis[N][N] , f[N] , flag , maxx;

struct node { int son[N][2] , mark[N]; } a[N];

int low[N] , dfn[N] , timer;
int scc[N] , id[N] , tot;
int in[N] , sta[N] , top;

vector<int> e[N] , ee[N];
inl void add ( int u , int v ) { e[u].eb(v); }
inl void adde ( int u , int v ) { ee[u].eb(v); }

void tarjan ( int u )
{
    dfn[u] = low[u] = ++ timer;
    sta[++top] = u , in[u] = 1;
    for ( auto v : e[u] )
    {
        if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
        else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
    }
    if ( low[u] == dfn[u] )
    {
        ++ tot;
        while ( top )
        {
            int x = sta[top--];
            id[x] = tot , in[x] = 0 , ++ scc[tot];
            if ( x == u ) break;
        }
    }
}

void check ( int u , int v , int x , int y )
{
    if ( vis[x][y] || !flag ) return;
    vis[x][y] = 1;
    if ( a[u].mark[x] && !a[v].mark[y] ) return flag = 0 , void();
    check ( u , v , a[u].son[x][0] , a[v].son[y][0] );
    check ( u , v , a[u].son[x][1] , a[v].son[y][1] );
}

void dp()
{
    for ( int u = tot ; u ; u -- )
        for ( auto v : ee[u] )
            f[v] = max ( f[v] , f[u] + scc[v] );
}

signed main ()
{
    ios::sync_with_stdio(false);
    cin.tie(0) , cout.tie(0);
    s = read();
    for ( int i = 1 ; i <= s ; i ++ )
    {
        n = read() , m = read();
        for ( int j = 1 ; j <= m ; j ++ ) a[i].mark[read()+1] = 1;
        for ( int j = 1 ; j <= n ; j ++ ) a[i].son[j][0] = read() + 1 , a[i].son[j][1] = read() + 1;
    }
    for ( int i = 1 ; i <= s ; i ++ )
        for ( int j = 1 ; j <= s ; j ++ )
            if ( i != j )
            {
                flag = 1 , memset ( vis , 0 , sizeof vis );
                check ( i , j , 1 , 1 );
                if ( flag ) add ( i , j ); 
            }
    for ( int i = 1 ; i <= s ; i ++ ) if ( !dfn[i] ) tarjan(i);
    for ( int u = 1 ; u <= s ; u ++ ) for ( auto v : e[u] ) if ( id[u] != id[v] ) adde ( id[u] , id[v] );
    for ( int i = 1 ; i <= tot ; i ++ ) f[i] = scc[i];
    dp();
    for ( int i = 1 ; i <= tot ; i ++ ) maxx = max ( maxx , f[i] );
    cout << maxx << endl;
    return 0;
}

P2941 [USACO09FEB] Surround the Islands

这题目是真的绕 相当于是统计一个点到其他所有点的边权 min 再乘上 2 (因为要走两次)

相当于是将整张图缩点 然后输入时将两个岛屿之间的边权取 min 即可

tarjan 写错了()

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int inf = 0x3f3f3f3f;
const int N = 5e3 + 5;
int read()
{
    int x = 0 , f = 1;
    char ch = getchar();
    while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
    while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
    return x * f;
}

int n , a[N][N] , ans = inf;

int low[N] , dfn[N] , timer;
int id[N] , tot;
int sta[N] , top , in[N];

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

void tarjan ( int u )
{
    dfn[u] = low[u] = ++ timer;
    sta[++top] = u , in[u] = 1;
    for ( auto v : e[u] )
    {
        if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
        else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
    }
    if ( low[u] == dfn[u] )
    {
        ++ tot;
        while ( top )
        {
            int x = sta[top--];
            id[x] = tot , in[x] = 0;
            if ( x == u ) break;
        }
    }
}

signed main ()
{
    ios::sync_with_stdio(false);
    cin.tie(0) , cout.tie(0);
    n = read();
    memset ( a , inf , sizeof a );
    for ( int i = 1 , u , v ; i <= n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
    for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i);
    for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 , val ; j <= n ; j ++ ) a[id[i]][id[j]] = min ( a[id[i]][id[j]] , read() );
    // for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= n ; j ++ ) cout << a[i][j] << ' ';
    for ( int i = 1 ; i <= tot ; i ++ )
    {
        int sum = 0;
        for ( int j = 1 ; j <= tot ; j ++ ) sum += a[i][j];
        ans = min ( ans , sum );
    }
    cout << ( ans << 1 ) << endl; 
    return 0;
}

P3225 [HNOI2012] 矿场搭建

三倍经验:

BUSINESS - Mining your own business

Mining Your Own Business

一道好题 我们可以先将所有的点双求出来 我们可以为它们建一棵树(不用实际去建)

考虑统计答案 如果这个连通块内没有割点 那么必须建立至少两个 保证一个被断之后另一个还可以发挥作用 方案个数就是 Cn2

如果这个连通块内有一个割点 说明是叶子节点 必须建立一个出口(在非割点上)因为这样如果断掉割点 其他点也可以跑 上面的点可以从它们父亲所能到达的其他叶子节点的出口逃跑 那么我们计入答案为 cnt+=1 sum=(sz1)

如果这个连通块内有两个以上割点 说明一个点都不需要建 因为如果一个割点断了 另一个割点一定没断 可以合并到另一个双连通分量中逃跑

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
#define int unsigned long long
const int N = 5e4 + 5;
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , cases , cnt , sum , cut[N];

int low[N] , dfn[N] , timer;
int id[N] , tot;
int sta[N] , top;

vector<int> e[N] , dcc[N];
inl void add ( int u , int v ) { e[u].eb(v); }

void tarjan ( int u , int rt )
{
	int child = 0;
	dfn[u] = low[u] = ++ timer;
	sta[++top] = u;
	for ( auto v : e[u] )
		if ( !dfn[v] )
		{
			tarjan(v,rt) , low[u] = min ( low[u] , low[v] );
			if ( dfn[u] <= low[v] )
			{
				++ child , ++ tot;
				if ( u != rt || child > 1 ) cut[u] = 1;
				while ( top )
				{
					int x = sta[top--];
					dcc[tot].eb(x);
					if ( x == v ) break;	
				}
				dcc[tot].eb(u);
			}
		}
		else low[u] = min ( low[u] , dfn[v] );
}

void init()
{
	for ( int i = 1 ; i <= n ; i ++ ) e[i].clear();
	for ( int i = 1 ; i <= tot ; i ++ ) dcc[i].clear();
	memset ( cut , 0 , sizeof cut );
	timer = top = tot = cnt = 0; sum = 1;
	memset ( dfn , 0 , sizeof dfn );
}
signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	while ( 1 )
	{
		init();
		m = read(); if ( !m ) break;
		for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u ) , n = max ( n , max ( u , v ) );
		for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i,i);
		for ( int i = 1 ; i <= tot ; i ++ )
		{
			int sz = dcc[i].size() , cntcut = 0;
			for ( auto v : dcc[i] ) cntcut += cut[v];
			if ( cntcut == 1 ) ++ cnt , sum *= sz - 1;
			if ( cntcut == 0 ) cnt += 2 , sum *= sz * ( sz - 1 ) / 2;
		}
		cout << "Case " << ++ cases << ": " << cnt << ' ' << sum << endl;
	}
	return 0;
}

P3119 [USACO15JAN] Grass Cownoisseur G

显然是首先来缩点

那么对于新图上的所有边 我们直接建立分层图 在加边 (u,v) 的时候直接加上一条 (v,u+n) 的反边 然后跑 spfa 即可 ( dij 貌似不能跑最长路 这样的贪心策略是有问题的 只能跑 spfa )

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 2e5 + 5;
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , u[N] , v[N];

int low[N] , dfn[N] , timer;
int id[N] , tot , scc[N];
int in[N] , sta[N] , top;

vector<int> e[N] , ee[N];
inl void add ( int u , int v ) { e[u].eb(v); }
inl void adde ( int u , int v ) { ee[u].eb(v); }

void tarjan ( int u )
{
	dfn[u] = low[u] = ++timer;
	sta[++top] = u , in[u] = 1;
	for ( auto v : e[u] )
	{
		if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
		else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
	}
	if ( dfn[u] == low[u] )
	{
		++ tot;
		while ( top )
		{
			int x = sta[top--];
			in[x] = 0 , id[x] = tot , ++ scc[tot];
			if ( x == u ) break;
		}
	}
}

queue<int> q;

int dis[N];

void spfa ()
{
	memset ( in , 0 , sizeof in );
	q.push(id[1]); in[id[1]] = 1;
	while ( !q.empty() )
	{
		int u = q.front(); q.pop(); in[u] = 0;
		for ( auto v : ee[u] )
			if ( dis[v] < dis[u] + scc[v] )
			{
				dis[v] = dis[u] + scc[v]; 
				if ( !in[v] ) q.push(v) , in[v] = 1;
			}
	}
}

signed main ()
{
	// freopen ( "a.in" , "r" , stdin );
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= m ; i ++ ) u[i] = read() , v[i] = read() , add ( u[i] , v[i] );
	for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i);
	for ( int i = 1 ; i <= m ; i ++ ) if ( id[u[i]] != id[v[i]] ) adde ( id[u[i]] , id[v[i]] ) , adde ( id[u[i]] + tot , id[v[i]] + tot ) , adde ( id[v[i]] , id[u[i]] + tot );
	for ( int i = 1 ; i <= tot ; i ++ ) scc[i+tot] = scc[i];
	spfa();
	cout << max ( dis[id[1]] , dis[tot+id[1]]) << endl;
	return 0;
}

P9431 [NAPC-#1] Stage3 - Jump Refreshers

一定注意 在图中 dp 定起点最长链的时候 一定不能像普通 tarjan 一样直接按照 dfs 序来搜索 而是要从该定点开始记忆化搜索(普通搜索会 T )

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define pii pair<int,int>
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 3e4 + 5;
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , d , s , x[N] , y[N] , f[N];

vector<int> e[N] , ee[N];
inl void add ( int u , int v ) { e[u].eb(v); }
inl void adde ( int u , int v ) { ee[u].eb(v); }

int dfn[N] , low[N] , timer;
int sta[N] , in[N] , top;
int id[N] , scc[N] , tot;

void tarjan ( int u )
{
	dfn[u] = low[u] = ++timer;
	in[sta[++top] = u] = 1;
	for ( auto v : e[u] ) 
	{
		if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
		else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
	}
	if ( dfn[u] == low[u] )
	{
		++ tot;
		while ( top )
		{
			int x = sta[top--];
			++ scc[tot] , in[x] = 0 , id[x] = tot;
			if ( x == u ) break;
		}
	}
}

int check ( int i , int j )
{
	return y[i] + d - y[j] >= abs ( x[i] - x[j] );
}

void init()
{
	for ( int i = 1 ; i <= n ; i ++ ) e[i].clear() , ee[i].clear();
	memset ( dfn , 0 , sizeof dfn );
	memset ( id , 0 , sizeof id );
	memset ( scc , 0 , sizeof scc );
	memset ( f , 0 , sizeof f );
	timer = tot = top = 0;
}

int dfs ( int u )
{
	if ( f[u] ) return f[u];
	f[u] = scc[u];
	for ( auto v : ee[u] )
		f[u] = max ( f[u] , scc[u] + dfs(v) );
	return f[u];
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read() , temp = read();
	while ( T -- )
	{
		init();
		n = read() , d = read() , s = read();
		for ( int i = 1 ; i <= n ; i ++ ) x[i] = read() , y[i] = read();
		for ( int i = 1 ; i <= n ; i ++ ) 
			for ( int j = 1 ; j <= n ; j ++ )
				if ( check ( i , j ) ) add ( i , j );
		for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i);
		for ( int u = 1 ; u <= n ; u ++ )
			for ( auto v : e[u] )
				if ( id[u] != id[v] ) adde ( id[u] , id[v] );
		cout << dfs(id[s]) << endl;
	}
	return 0;
}

[P2515 HAOI2010] 软件安装

对于一个环内的东西 要么都安装 要么都不安装

那么缩点之后进行树上背包即可()

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define pii pair<int,int>
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , v[N] , w[N] , rd[N] , f[N][N];

vector<int> e[N] , ee[N];
inl void add ( int u , int v ) { e[u].eb(v); }
inl void adde ( int u , int v ) { ee[u].eb(v); }

int dfn[N] , low[N] , timer;
int sta[N] , in[N] , top;
int id[N] , sccv[N] , sccw[N] , tot;

void tarjan ( int u )
{
    dfn[u] = low[u] = ++ timer;
    sta[++top] = u , in[u] = 1;
    for ( auto v : e[u] )
        if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
        else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
    if ( dfn[u] == low[u] )
    {
        ++ tot;
        while ( top )
        {
            int x = sta[top--];
            id[x] = tot , sccv[tot] += v[x] , sccw[tot] += w[x] , in[x] = 0;
            if ( x == u ) break;
        }
    }
}

void dfs ( int u , int ff )
{
    for ( int i = sccv[u] ; i <= m ; i ++ ) f[u][i] = sccw[u];
    for ( auto v : ee[u] )
    {
        dfs ( v , u );
        for ( int j = m ; j >= sccv[u] ; j -- )
            for ( int k = sccv[v] ; k <= j - sccv[u] ; k ++ )
                f[u][j] = max ( f[u][j] , f[u][j-k] + f[v][k] );
    }
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
    n = read() , m = read();
    for ( int i = 1 ; i <= n ; i ++ ) v[i] = read();
    for ( int i = 1 ; i <= n ; i ++ ) w[i] = read();
    for ( int i = 1 , ff ; i <= n ; i ++ ) ff = read() , add ( ff , i );
    for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i);
    for ( int u = 1 ; u <= n ; u ++ )
        for ( auto v : e[u] ) if ( id[u] != id[v] ) adde ( id[u] , id[v] ) , ++ rd[id[v]];
    for ( int i = 1 ; i <= tot ; i ++ ) if ( !rd[i] ) adde ( 0 , i );
    dfs ( 0 , 0 );
    cout << f[0][m] << endl;
    return 0;
}
posted @   Echo_Long  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示