传送门:【CodeForces】467D Fedor and Essay
题目分析:首先将单词离散化(我用的map),然后根据题意对单词u能替换成单词v的我们建有向边(u,v),然后跑一次强连通,在强连通里面如果子节点比父节点更优则用子节点的信息来取代父节点的。然后当dfn[ u ] == low[ u ]时,也就是形成一个强连通的时候,很容易可以得知u的信息一定是u能取到的最优的转化(已经用子节点更新自己了,等于所有子节点的信息都以上传,可以自己yy一下),然后强连通的缩点(就当他缩点了吧)同时用u的信息更新所有的该强连通中其他点的信息。
tarjan遍历选取第一个点遍历的先后顺序是不会影响结果的,可以yy一下。
最后答案只要累加该单词替换成的单词的信息即可(当然可能有的单词不替换)。
是不是很简单?但是有一个坑点,就是会爆int!
这个应该也算DP吧?。。既然cf都说是DP了那我也就不客气了,我贫瘠的DP终于又补充了一道~
代码如下:
#include <map> #include <string> #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; typedef long long LL ; #define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next ) #define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i ) #define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i ) #define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i ) #define clr( a , x ) memset ( a , x , sizeof a ) #define cpy( a , x ) memcpy ( a , x , sizeof a ) #define F( i ) ( ( ( i ) - 1 ) % n + 1 ) const int MAXN = 100005 ; const int MAXE = 100005 ; struct Edge { int v ; Edge* next ; } E[MAXE] , *H[MAXN] , *edge ; struct Node { int idx ; int Rnum , Len ; Node () {} Node ( int N , int L , int idx ) : Rnum ( N ) , Len ( L ) , idx ( idx ) {} bool operator < ( const Node& a ) const { if ( Rnum != a.Rnum ) return Rnum < a.Rnum ; return Len < a.Len ; } } word[MAXN] , a[MAXN] ; int dfn[MAXN] , low[MAXN] , dfs_clock ; int scc[MAXN] , scc_cnt ; map < string , int > mp ; int S[MAXN] , top ; char buf[5 * MAXN] ; int point ; int n , m ; void clear () { top = 0 ; edge = E ; point = 0 ; mp.clear () ; scc_cnt = 0 ; dfs_clock = 0 ; clr ( H , 0 ) ; clr ( dfn , 0 ) ; clr ( scc , 0 ) ; } void addedge ( int u , int v ) { edge -> v = v ; edge -> next = H[u] ; H[u] = edge ++ ; } void tarjan ( int u ) { dfn[u] = low[u] = ++ dfs_clock ; S[top ++] = u ; travel ( e , H , u ) { int v = e -> v ; if ( !dfn[v] ) { tarjan ( v ) ; low[u] = min ( low[u] , low[v] ) ; } else if ( !scc[v] ) low[u] = min ( low[u] , dfn[v] ) ; if ( a[v] < a[u] ) a[u] = a[v] ; } if ( low[u] == dfn[u] ) { ++ scc_cnt ; do { int v = S[-- top] ; scc[v] = scc_cnt ; a[v] = a[u] ; } while ( S[top] != u ) ; } } Node read () { int len = 0 , num = 0 ; char c ; while ( ( c = getchar () ) == ' ' || c == '\n' ) ; if ( c <= 'Z' ) c += 97 - 'A' ; if ( c == 'r' ) ++ num ; buf[len ++] = c ; while ( ( c = getchar () ) >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' ) { if ( c <= 'Z' ) c += 97 - 'A' ; buf[len ++] = c ; if ( c == 'r' ) ++ num ; } buf[len] = 0 ; map < string , int > :: iterator it = mp.find ( buf ) ; if ( it != mp.end () ) return Node ( num , len , it -> second ) ; mp.insert ( map < string , int > :: value_type ( buf , point ) ) ; a[point] = Node ( num , len , 0 ) ; return Node ( len , num , point ++ ) ; } void solve () { clear () ; rep ( i , 0 , n ) word[i] = read () ; scanf ( "%d" , &m ) ; while ( m -- ) { int u = read ().idx ; int v = read ().idx ; addedge ( u , v ) ; } rep ( i , 0 , point ) if ( !dfn[i] ) tarjan ( i ) ; LL ans1 = 0 , ans2 = 0 ; rep ( i , 0 , n ) { ans1 += a[word[i].idx].Rnum ; ans2 += a[word[i].idx].Len ; } printf ( "%I64d %I64d\n" , ans1 , ans2 ) ; } int main () { while ( ~scanf ( "%d" , &n ) ) solve () ; return 0 ; }