• [CF599E] Sandy and Nuts


    题目

    点这里看题目。

    分析

    首先我们可以考虑不存在任何限制的时候该怎么做。

    根据 (Proverset{..}{u}fer) 序列直接得出答案为 (n^{n-2})

    忽略那种做法,因为它难以处理 LCA 的限制。看到 (n) 很小,我们可以想到一个状态压缩的 DP :

    (f(u,S)):以 (u) 为根的子树中点集为 (S) 的方案数。

    为了方便,以下我们也就用 ((u,S)) 表示一棵根为 (u) ,点集为 (S) 的子树。

    之后,边界条件为 (f(u,{u})=1) ,答案就是 (f(1,V))

    然后存在转移:

    [f(u,S)=sum_{vin Tsubseteq S} f(u,Ssetminus T) imes f(v,T) ]

    直接转移会重复计数。例如这种情况:

    ((u,S)) 有两棵子树 ((v,S_1))((w,S_2)) ,满足 (S=S_1cup S_2cup {u})

    那么 (f(u,Ssetminus S_1) imes f(v,S_1))(f(u,Ssetminus S_2) imes f(w,S_2)) 都会计算这种情况。

    为了规避重复计算,我们需要钦定一个不为 (u) 特殊点,每次只枚举包含特殊点的 ((v,T)) 进行转移

    这就相当于始终在枚举特殊点所在的子树的情况,可以发现这样可以做到不重不漏。

    那么考虑题目给出的限制,实际上就是在限制 DP 的转移。分开考虑:

    先考虑 LCA 的限制等价于:

    1. 对于树 ((u, S)),如果存在 (k) ,使得 (c_k=u) ,那么 (a_i,b_i) 不能在同一个子树内,即不能进行 (a_iin Tland b_iin T) 的转移。
    2. 对于转移的子树 ((v, T)), 如果存在 (k) ,使得 (c_kin T) ,那么 (a_i,b_i) 也应该在 ((v,T)) 里面,即不能进行 (a_i otin Tlor b_i otin T) 的转移。

    再考虑边的限制。一条边((u,v))同时包含了 (u)(v) 的父亲(v)(u) 的父亲 两种情况,而前者在 (f(u,S)) 中计算,后者在 (f(v,S)) 中计算。故转移中我们只需要考虑 (u) 作为父亲的情况。

    因而一条边等价于:

    1. 对于树 ((u,S)),如果不存在 (k) 使得 (u) 在限制边 (E_k=(x,y)) 上,那么转移的子树中,要么 (x otin Tland y ot in T) ,要么 (xin Tland yin T)
    2. 对于树 ((u,S)),如果存在 (k) ,使得 (u) 在限制边 (E_k=(u,w)) 上,那么转移的子树只能((w, T)) ,而且 (T) 里面只能包含 (w) 一个与 (u) 有关的限制边端点

    在转移中途判断一下即可。同样的道理,写状压尽量记搜,因为状态的阶段通常是由 (|S|) 划分的。

    时间是 (O(3^nk(n+m+q)))CF 机子是出了名的快。

    代码

    #include <cstdio>
    #include <cstring>
    
    typedef long long LL;
    
    const int MAXN = 15, MAXM = 105, MAXS = ( 1 << 13 ) + 5;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0;char s = getchar();int f = 1;
    	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
    	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
    	x *= f;
    }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
    	if( 9 < x ){ write( x / 10 ); }
    	putchar( x % 10 + '0' );
    }
    
    LL f[MAXN][MAXS];
    int LCARis[MAXM], LCAc[MAXM];
    int Eu[MAXM], Ev[MAXM], ERis[MAXN];
    int bit[MAXS], ref[MAXN];
    int N, M, Q;
    
    int mono( const int u ) { return 1 << u - 1; }
    int lowbit( const int x ) { return x & ( -x ); }
    int inter( const int S, const int T ) { return S & T; }
    bool chk( const int S ) { return ! ( S - lowbit( S ) ); }
    bool in( const int S, const int T ) { return ( S & T ) == T; }
    bool onE( const int u, const int id ) { return Eu[id] == u || Ev[id] == u; }
    
    LL DFS( const int u, const int S )
    {
    	LL& ret = f[u][S];
    
    	if( ~ ret ) return ret; ret = 0;
    	if( ! in( S, ref[u] ) ) return ret = 0;
    	
    	int pos; bool flag;
    	for( pos = 1 ; pos <= N ; pos ++ ) 
    		if( pos ^ u && in( S, ref[pos] ) ) break;
    	for( int T = ( S - 1 ) & S ; T ; T = ( T - 1 ) & S )
    		if( in( T, ref[pos] ) && ! in( T, ref[u] ) )
    		{
    			flag = true;
    			for( int i = 1 ; i <= Q ; i ++ )
    				if( LCAc[i] == u && in( T, LCARis[i] ) )
    					{ flag = false; break; }
                            //处理限制 1
    			if( ! flag ) continue;
    			for( int i = 1 ; i <= Q ; i ++ )
    				if( in( T, ref[LCAc[i]] ) && ! in( T, LCARis[i] ) )
    					{ flag = false; break; }
                            //处理限制 2
    			if( ! flag ) continue;
    			for( int i = 1 ; i <= M ; i ++ )
    				if( ! onE( u, i ) && ( in( T, ref[Eu[i]] ) ^ in( T, ref[Ev[i]] ) ) )
    					{ flag = false; break; }
                            //处理限制 3
    			if( ! flag ) continue;
    			if( ! inter( T, ERis[u] ) )
    			{
    				for( int i = 1 ; i <= N ; i ++ )
    					if( in( T, ref[i] ) )
    						ret += DFS( u, S ^ T ) * DFS( i, T );
    			}
                            //如果不包含任何限制边就枚举根进行转移
    			else if( chk( T & ERis[u] ) )
    				ret += DFS( u, S ^ T ) * DFS( bit[lowbit( T & ERis[u] )], T );
                            //否则需要钦定根转移  
    		}
    	return ret;
    }
    
    int main()
    {
    	read( N ), read( M ), read( Q );
    	memset( f, -1, sizeof f );
    	for( int i = 1 ; i <= N ; i ++ ) f[i][1 << i - 1] = 1, bit[ref[i] = ( 1 << i - 1 )] = i;
    	for( int i = 1 ; i <= M ; i ++ ) 
    		read( Eu[i] ), read( Ev[i] ), 
    		ERis[Eu[i]] |= ref[Ev[i]], ERis[Ev[i]] |= ref[Eu[i]];
    	for( int i = 1, a, b ; i <= Q ; i ++ ) 
    		read( a ), read( b ), read( LCAc[i] ), 
    		LCARis[i] = ref[a] | ref[b];
    	write( DFS( 1, ( 1 << N ) - 1 ) ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    TCP/IP、UDP、HTTP详解
    第一章 Shiro简介(学习笔记)
    [已失效]坦白说(查看好友)抓包教程+解密工具
    常用正则表达式
    计算机网络基础
    三层交换机配置实例
    计算机网络基础3
    计算机网络基础4
    计算机基础2
    Linux基础命令练习1
  • 原文地址:https://www.cnblogs.com/crashed/p/13395632.html
Copyright © 2020-2023  润新知