2014 Asia AnShan Regional Contest 题解

B:5071Chat

暴力模拟即可。注意Bye的时候先和always on top说bye。还有注意long long。


代码如下:


#include 
#include 
#include 
#include 
using namespace std ;

#pragma comment(linker, "/STACK:16777216")
#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 )

typedef long long LL ;

const int MAXN = 5005 ;

struct Node {
	int pre , next ;
	int x ;
	LL v ;
	Node () {}
	Node ( int x , LL v ) : x ( x ) , v ( v ) {}
} ;

Node node[MAXN] ;
int head , tail ;
set < int > s ;
int cur ;
int size ;
int top ;

void link ( int u , int v ) {
	node[u].next = v ;
	node[v].pre = u ;
}

void clear () {
	cur = 1 ;
	size = 0 ;
	head = 0 ;
	tail = 1 ;
	top = 0 ;
	link ( head , tail ) ;
	s.clear () ;
}

void Add () {
	int x ;
	scanf ( "%d" , &x ) ;
	if ( s.count ( x ) ) printf ( "same priority.\n" ) ;
	else {
		s.insert ( x ) ;
		++ size ;
		node[++ cur] = Node ( x , 0 ) ;
		link ( node[tail].pre , cur ) ;
		link ( cur , tail ) ;
		printf ( "success.\n" ) ;
	}
}

void Close () {
	int x ;
	scanf ( "%d" , &x ) ;
	if ( !s.count ( x ) ) printf ( "invalid priority.\n" ) ;
	else {
		s.erase ( x ) ;
		-- size ;
		if ( top == x ) top = 0 ;
		for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
			if ( node[i].x == x ) {
				printf ( "close %d with %I64d.\n" , x , node[i].v ) ;
				link ( node[i].pre , node[i].next ) ;
				return ;
			}
		}
	}
}

void Chat () {
	int x ;
	scanf ( "%d" , &x ) ;
	if ( !size ) printf ( "empty.\n" ) ;
	else {
		if ( top ) {
			for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
				if ( node[i].x == top ) {
					node[i].v += x ;
					break ;
				}
			}
		} else node[node[head].next].v += x ;
		printf ( "success.\n" ) ;
	}
}

void Rotate () {
	int x ;
	scanf ( "%d" , &x ) ;
	if ( x > size || x < 1 ) printf ( "out of range.\n" ) ;
	else {
		for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
			if ( 0 == -- x ) {
				link ( node[i].pre , node[i].next ) ;
				link ( i , node[head].next ) ;
				link ( head , i ) ;
				printf ( "success.\n" ) ;
				return ;
			}
		}
	}
}

void Prior () {
	if ( !size ) printf ( "empty.\n" ) ;
	else {
		int x = *( -- s.end () ) ;
		for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
			if ( x == node[i].x ) {
				link ( node[i].pre , node[i].next ) ;
				link ( i , node[head].next ) ;
				link ( head , i ) ;
				printf ( "success.\n" ) ;
				return ;
			}
		}
	}
}

void Choose () {
	int x ;
	scanf ( "%d" , &x ) ;
	if ( !s.count ( x ) ) printf ( "invalid priority.\n" ) ;
	else {
		for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
			if ( x == node[i].x ) {
				link ( node[i].pre , node[i].next ) ;
				link ( i , node[head].next ) ;
				link ( head , i ) ;
				printf ( "success.\n" ) ;
				return ;
			}
		}
	}
}

void Top () {
	int x ;
	scanf ( "%d" , &x ) ;
	if ( !s.count ( x ) ) printf ( "invalid priority.\n" ) ;
	else {
		top = x ;
		printf ( "success.\n" ) ;
	}
}

void Untop () {
	if ( !top ) printf ( "no such person.\n" ) ;
	else top = 0 , printf ( "success.\n" ) ;
}

void Bye () {
	if ( top ) {
		for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
			if ( top == node[i].x ) {
				if ( node[i].v ) printf ( "Bye %d: %I64d\n" , node[i].x , node[i].v ) ;
				break ;
			}
		}
	}
	for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
		if ( top == node[i].x ) continue ;
		if ( node[i].v ) printf ( "Bye %d: %I64d\n" , node[i].x , node[i].v ) ;
	}
}

void debug () {
	for ( int i = node[head].next ; i != tail ; i = node[i].next ) {
		printf ( "x = %d v = %I64d\n" , node[i].x , node[i].v ) ;
	}
}

void solve () {
	clear () ;
	int n ;
	char op[10] ;
	scanf ( "%d" , &n ) ;
	For ( i , 1 , n ) {
		printf ( "Operation #%d: " , i ) ;
		scanf ( "%s" , op ) ;
		if ( !strcmp ( op , "Add" ) ) Add () ;
		if ( !strcmp ( op , "Close" ) ) Close () ;
		if ( !strcmp ( op , "Chat" ) ) Chat () ;
		if ( !strcmp ( op , "Rotate" ) ) Rotate () ;
		if ( !strcmp ( op , "Prior" ) ) Prior () ;
		if ( !strcmp ( op , "Choose" ) ) Choose () ;
		if ( !strcmp ( op , "Top" ) ) Top () ;
		if ( !strcmp ( op , "Untop" ) ) Untop () ;
		//debug () ;
	}
	Bye () ;
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0;
}


C:5072 Coprime

容斥原理。

我们可以假设这n个点构成一个完全图:互质的两点间建边权值为1,不互质的两点间建边权值为0,则题目要求即三边边权都为0或都为1的三角形个数。

直接求不好求,我们求命题的反面:两条边边权为1、一条边边权为0的三角形个数+两条边边权为0、一条边边权为1的三角形个数。

我们知道以一个点为顶点的符合条件(此时我们指反面的命题)的三角形个数即边权为0的边的个数*边权为1的边的个数,由于从一个点出发的边一定是n-1条,所以我们只要求出边权为0的边的个数或者边权为1的边的个数即可,另一个可以直接得出。由于同一个三角形一定会被两个顶点重复计算(想想为什么一定是两个?而不是更少或更多),所以计算出的以每个点出发的三角形个数的总和sum的一半才是符合条件的三角形个数(反面命题)。

则原题的答案即:C(n,3)- sum / 2 。


代码如下:


#include 
#include 
#include 
using namespace std ;

#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 )

typedef long long LL ;

const int MAXN = 100005 ;
const int MAXE = 1000005 ;

struct Edge {
	int v , n ;
	Edge () {}
	Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;

Edge E[MAXE] ;
int H[MAXN] , cntE ;
bool hash[MAXN] ;
bool prime[MAXN] ;
int cnt[MAXN] ;
int num[MAXN] ;
int vis[MAXN] ;
int Time ;
int n ;
LL ans , tmp ;

void addedge ( int u , int v ) {
	E[cntE] = Edge ( v , H[u] ) ;
	H[u] = cntE ++ ;
}

void get_prime () {
	cntE = 0 ;
	clr ( H , -1 ) ;
	clr ( prime , true ) ;
	for ( int i = 2 ; i < MAXN ; ++ i ) {
		if ( prime[i] ) {
			addedge ( i , i ) ;
			for ( int j = i + i ; j < MAXN ; j += i ) {
				addedge ( j , i ) ;
				prime[j] = 0 ;
			}
		}
	}
}

void preprocess () {
	rep ( i , 2 , MAXN ) {
		for ( int j = i ; j < MAXN ; j += i ) {
			if ( hash[j] ) ++ cnt[i] ;
		}
	}
}

void dfs ( int u , int val , int sign ) {
	for ( int i = H[u] ; ~i ; i = E[i].n ) {
		int v = E[i].v ;
		if ( u % v == 0 && vis[val * v] != Time ) {
			vis[val * v] = Time ;
			tmp += sign * ( cnt[val * v] - 1 ) ;
			dfs ( u / v , val * v , -sign ) ;
		}
	}
}

void solve () {
	clr ( hash , false ) ;
	clr ( cnt , 0 ) ;
	ans = 0 ;
	scanf ( "%d" , &n ) ;
	rep ( i , 0 , n ) {
		scanf ( "%d" , &num[i] ) ;
		hash[num[i]] = true ;
	}
	preprocess () ;
	rep ( u , 0 , n ) {
		int x = 1 ;
		tmp = 0 ;
		for ( int i = H[num[u]] ; ~i ; i = E[i].n ) x *= E[i].v ;
		++ Time ;
		dfs ( x , 1 , 1 ) ;
		ans += ( n - 1 - tmp ) * tmp ;
		//printf ( "%lld\n" , tmp ) ;
	}
	printf ( "%I64d\n" , ( LL ) n * ( n - 1 ) * ( n - 2 ) / 6 - ans / 2 ) ;
}

int main () {
	int T ;
	get_prime () ;
	clr ( vis , 0 ) ;
	Time = 0 ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}




D:5073 Galaxy

O(N)递推。

首先我们要知道选择的n-k个一定位置是连续的(这个应该不难想到),然后我们先求出1~n-k的平方和,然后用递推得到所有i~n-k+i-1的平方和,取最小值即可。

首先对位置排序,接下来用的位置均为排序后的位置。

以i为开头的连续n-k个的重心为( sum[n - k + i - 1] - sum[i - 1] ) / ( n - k )。

设第i位置上的数为num[i]

预处理出1~i的前缀和sum[i],1~i的前缀平方和sqr[i]。

设以i开头的重心为pre,以i+1开头的重心为now。

则以i开头的公式化简的结果为sqr[n - k + i - 1] - sqr[i - 1] - 2 * pre * ( sum[n - k + i - 1] - sum[i - 1] ) + ( n - k ) * pre * pre。

则以i+1开头的公式化简的结果为sqr[n - k + i] - sqr[i] - 2 * now * ( sum[n - k + i] - sum[i] ) + ( n - k ) * now - now。

则由i推到i+1时的增量为i+1的式子减i的式子,化简得:num[n - k + i] * num[n - k + i] - num[i] * num[i] - 2 * pre * ( num[n - k + i] - num[i] ) - 2 * ( now - pre ) * ( sum[n - k + i] - sum[i] ) + ( n - k ) * ( now * now - pre * pre )。

注意当n=k时直接输出0。


代码如下:


#include 
#include 
#include 
#include 
using namespace std ;

typedef long long LL ;

#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 100005 ;

double num[MAXN] ;
double sum[MAXN] ;
double sqr[MAXN] ;
int n , k ;

void solve () {
	sum[0] = sqr[0] = 0 ;
	scanf ( "%d%d" , &n , &k ) ;
	For ( i , 1 , n ) scanf ( "%lf" , &num[i] ) ;
	sort ( num + 1 , num + n + 1 ) ;
	For ( i , 1 , n ) {
		sum[i] = sum[i - 1] + num[i] ;
		sqr[i] = sqr[i - 1] + num[i] * num[i] ;
	}
	if ( n == k ) {
		printf ( "0\n" ) ;
		return ;
	}
	double pre = sum[n - k] / ( n - k ) ;
	double ans = sqr[n - k] - 2 * pre * sum[n - k] + ( n - k ) * pre * pre ;
	double tmp = ans ;
	//printf ( "%.10f\n" , ans ) ;
	For ( i , 1 , k ) {
		double now = ( sum[n - k + i] - sum[i] ) / ( n - k ) ;
		tmp += num[n - k + i] * num[n - k + i] - num[i] * num[i] ;
		tmp += ( n - k ) * ( now * now - pre * pre ) ;
		tmp -= 2 * pre * ( num[n - k + i] - num[i] ) ;
		tmp -= 2 * ( now - pre ) * ( sum[n - k + i] - sum[i] ) ;
		ans = min ( ans , tmp ) ;
		pre = now ;
	}
	printf ( "%.10f\n" , ans ) ;
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}

E:5074 Hatsune Miku

简单DP。

设a[i]表示未处理前位置i上的数。

设dp[i][j]表示处理完第i个位置,第i个位置上数为j时的最大收益。那么:

dp[i][j] = max ( dp[i][j] , dp[i - 1][k] + G[k][j] )                    ( a[i - 1] = -1 , a[i] = -1 )

dp[i][j] = max ( dp[i][j] , dp[i - 1][a[i - 1]] + G[a[i - 1]][j] )    ( a[i - 1] != -1 , a[i] = -1 )

dp[i][a[i]] = max ( dp[i][a[i]] , dp[i - 1][j] + G[j][a[i]] )          ( a[i - 1] = -1 , a[i] != -1 )

dp[i][a[i]] = dp[i - 1][a[i - 1]] + G[a[i - 1]][a[i]]                     ( a[i - 1] != -1 , a[i] != -1 )


代码如下:


#include 
#include 
#include 
#include 
using namespace std ;

#pragma comment(linker, "/STACK:16777216")
#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 )

int dp[105][55] ;
int G[55][55] ;
int a[105] ;
int n , m ;

void scanf ( int& x , char c = 0 ) {
	while ( ( c = getchar () ) < '0' || c > '9' ) ;
	x = c - '0' ;
	while ( ( c = getchar () ) >= '0' && c <= '9' ) x = x * 10 + c - '0' ;
}

void solve () {
	clr ( dp , 0 ) ;
	scanf ( n ) ;
	scanf ( m ) ;
	For ( i , 1 , m ) For ( j , 1 , m ) scanf ( G[i][j] ) ;
	For ( i , 1 , n ) scanf ( "%d" , &a[i] ) ;
	For ( i , 2 , n ) {
		if ( ~a[i] ) {
			if ( ~a[i - 1] ) {
				dp[i][a[i]] = dp[i - 1][a[i - 1]] + G[a[i  -1]][a[i]] ;
			} else {
				For ( j , 1 , m ) {
					dp[i][a[i]] = max ( dp[i][a[i]] , dp[i - 1][j] + G[j][a[i]] ) ;
				}
			}
		} else {
			if ( ~a[i - 1] ) {
				For ( j , 1 , m ) {
					dp[i][j] = max ( dp[i][j] , dp[i - 1][a[i - 1]] + G[a[i - 1]][j] ) ;
				}
			} else {
				For ( j , 1 , m ) {
					For ( k , 1 , m ) {
						dp[i][j] = max ( dp[i][j] , dp[i - 1][k] + G[k][j] ) ;
					}
				}
			}
		}
	}
	if ( ~a[n] ) printf ( "%d\n" , dp[n][a[n]] ) ;
	else {
		int ans = 0 ;
		For ( i , 1 , m ) ans = max ( ans , dp[n][i] ) ;
		printf ( "%d\n" , ans ) ;
	}
}

int main () {
	int T ;
	scanf ( T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}


H:5077 NAND

打表题。。。

用初始状态a(15),b(51),c(85),全0(0),全1(255)去bfs出所有的状态,每次取出已经有的两个状态去与非来得到新的状态。

状态104我的电脑打不出来,最后枚举了几下过的。


代码如下:



#include 
#include 
#include 
#include 
#include 
#include 
using namespace std ;

#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

typedef long long LL ;

const int MAXN = 300 ;

struct Node {
	vector < int > a ;
	int depth , size ;
	void init () {
		depth = 1 ;
		a.push_back ( 0 ) ;
		a.push_back ( 15 ) ;
		a.push_back ( 51 ) ;
		a.push_back ( 85 ) ;
		a.push_back ( 255 ) ;
	}
	bool search ( int x ) {
		int l = 0 , r = a.size () - 1 ;
		while ( l < r ) {
			int m = ( l + r ) >> 1 ;
			if ( a[m] >= x ) r = m ;
			else l = m + 1 ;
		}
		return a[l] == x ;
	}
} ;

map < vector < int > , int > mp ;
queue < Node > q ;
int d[300] = { 1 , 5 , 6 , 3 , 6 , 3 , 7 , 4 , 7 , 8 , 4 , 5 , 4 , 5 , 4 , 1 , 6 , 3 , 7 , 4 , 7 , 4 , 9 , 7 , 8 , 8 , 7 , 5 , 7 , 5 , 7 , 4 , 7 , 8 , 4 , 5 , 8 , 8 , 7 , 5 , 8 , 9 , 5 , 6 , 8 , 8 , 5 , 5 , 4 , 5 , 4 , 1 , 7 , 5 , 7 , 4 , 8 , 8 , 5 , 5 , 5 , 7 , 6 , 4 , 7 , 8 , 8 , 8 , 4 , 5 , 7 , 5 , 8 , 9 , 8 , 8 , 5 , 6 , 5 , 5 , 4 , 5 , 7 , 5 , 4 , 1 , 7 , 4 , 8 , 8 , 5 , 7 , 5 , 5 , 6 , 4 , 8 , 9 , 8 , 8 , 8 , 8 , 5 , 7 , 11 , 9 , 8 , 9 , 8 , 9 , 8 , 8 , 5 , 6 , 5 , 5 , 5 , 5 , 6 , 4 , 8 , 9 , 8 , 8 , 8 , 8 , 8 , 7 , 8 , 9 , 9 , 9 , 9 , 9 , 10 , 9 , 5 , 7 , 6 , 6 , 6 , 6 , 7 , 6 , 9 , 9 , 10 , 9 , 10 , 9 , 10 , 10 , 7 , 6 , 7 , 7 , 7 , 7 , 9 , 7 , 5 , 7 , 6 , 6 , 7 , 6 , 7 , 7 , 5 , 6 , 2 , 3 , 6 , 6 , 4 , 3 , 6 , 6 , 7 , 6 , 7 , 7 , 9 , 7 , 6 , 6 , 4 , 3 , 7 , 7 , 7 , 6 , 5 , 7 , 7 , 6 , 6 , 6 , 7 , 7 , 5 , 6 , 6 , 6 , 2 , 3 , 4 , 3 , 6 , 6 , 7 , 7 , 7 , 6 , 9 , 7 , 6 , 6 , 7 , 7 , 4 , 3 , 7 , 6 , 5 , 6 , 6 , 6 , 6 , 6 , 7 , 7 , 8 , 9 , 5 , 6 , 5 , 6 , 2 , 5 , 2 , 3 , 4 , 3 , 4 , 3 , 7 , 6 , 5 , 6 , 2 , 5 , 2 , 5 , 4 , 1 } ;
int cnt ;
FILE* fp ;

inline int NAND ( int a , int b ) {
	return ( a & b ) ^ 255 ;
}

inline bool check () {
	int cnt = 0 ;
	rep ( i , 0 , 256 ) if ( -1 == d[i] ) ++ cnt ;
	if ( cnt > 1 ) return 0 ;
	fprintf ( fp , "d[300] = { " ) ;
	rep ( i , 0 , 256 ) fprintf ( fp , "%d , " , d[i] ) ;
	fprintf ( fp , "} ;\n" ) ;
	return 1 ;
}

inline void show ( int x ) {
	printf ( "%d\n" , ++ cnt ) ;
	printf ( "index :%5d\n" , x ) ;
	printf ( "depth :%5d\n" , d[x] ) ;
	printf ( "\n" ) ;
}

void fun () {
	mp.clear () ;
	while ( !q.empty () ) q.pop () ;
	clr ( d , -1 ) ;
	cnt = 0 ;
	Node s ;
	Node u , v ;
	s.init () ;
	d[0] = d[15] = d[51] = d[85] = d[255] = 1 ;
	show ( 0 ) ;
	show ( 15 ) ;
	show ( 51 ) ;
	show ( 85 ) ;
	show ( 255 ) ;
	q.push ( s ) ;
	while ( !check () ) {
		u = q.front () ;
		q.pop () ;
		int size = u.a.size () ;
		rep ( i , 0 , size ) {
			rep ( j , 0 , size ) {
				int tmp = NAND ( u.a[i] , u.a[j] ) ;
				if ( d[tmp] == -1 ) {
					d[tmp] = u.depth + 1 ;
					show ( tmp ) ;
				}
				if ( !u.search ( tmp ) ) {
					v = u ;
					v.a.push_back ( tmp ) ;
					++ v.depth ;
					sort ( v.a.begin () , v.a.end () ) ;
					if ( mp[v.a] == 1 ) continue ;
					mp[v.a] = 1 ;
					q.push ( v ) ;
				}
			}
		}
	}
	printf ( "ok\n" ) ;
}

int encode ( int x ) {
	int ans = 0 ;
	rep ( i , 0 , 8 ) {
		if ( x % 10 ) ans += 1 << i ;
		x /= 10 ;
	}
	return ans ;
}

int main () {
	//fp = fopen ( "out.txt" , "w" ) ;
	int T , x ;
	//fun () ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) printf ( "%d\n" , ( scanf ( "%d" , &x ) , d[encode ( x )] ) ) ;
	return 0 ;
}

I:5078 Osu!

签到题。


代码如下:


#include 
#include 
#include 
#include 
#include 
using namespace std ;

#pragma comment(linker, "/STACK:16777216")
#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 )

const int MAXN = 1000005 ;

double t[MAXN] , x[MAXN] , y[MAXN] ;

double dist ( double x , double y ) {
	return sqrt ( x * x + y * y ) ;
}

void solve () {
	int n ;
	double ans = 0 ;
	scanf ( "%d" , &n ) ;
	For ( i , 1 , n ) scanf ( "%lf%lf%lf" , &t[i] , &x[i] , &y[i] ) ;
	For ( i , 2 , n ) ans = max ( ans , dist ( x[i] - x[i - 1] , y[i] - y[i - 1] ) / ( t[i] - t[i - 1] ) ) ;
	printf ( "%.10f\n" , ans ) ;
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}


K:5080 Colorful Toy

置换群。ploya计数。

首先求出n个点的重心,然后将该图形移到原点(当前坐标减去重心坐标),然后我们枚举角度旋转然后判断能否得到置换群(由于不可能存在某个坐标绕原点旋转不为90度的倍数时旋转后坐标依旧是整数(能证明吗?),于是乎我们枚举90,180,270度看是否能得到置换群即可(0度下一定存在置换群),首先判断点旋转后能否变成另一个点,然后判断边旋转后能否变成另一条边,如果都能做到,说明存在置换群,用O(n)求循环节的方法求得该置换的循环节个数g。则题目的答案就是(m^g1 + m^g2 + ... + m^gs)/s,其中s为存在的置换的个数,gi为第i个置换的循环节个数,m为不同颜色的个数。(对该计数公式不懂的话就去搜索吧~,网上很多的)

由于涉及到除法以及取模,所以预处理出s为1,2,3,4时对mod的逆元。


代码如下:


#include 
#include 
#include 
using namespace std ;

#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

typedef long long LL ;

const int MAXN = 55 ;
const int mod = 1e9 + 7 ;
const double eps = 1e-18 ;

struct Point {
	double x , y ;
	Point () {}
	Point ( double _x , double _y ) : x ( _x ) , y ( _y ) {}
	void input () {
		scanf ( "%lf%lf" , &x , &y ) ;
	}
} ;

struct Seg {
	int p1 , p2 ;
	void input () {
		scanf ( "%d%d" , &p1 , &p2 ) ;
	}
} ;

Point p[MAXN] , p2[MAXN] ;
Seg L[MAXN * MAXN] ;
int G[MAXN][MAXN] ;
int link[MAXN] ;
int vis[MAXN] , Time ;
LL Pow[MAXN] ;
int Cos[4] = { 1 , 0 , -1 , 0 } ;//0,90,180,270
int Sin[4] = { 0 , 1 , 0 , -1 } ;//0,90,180,270
LL inv[5] = { 0 , 1 , 500000004 , 333333336 , 250000002 } ;
int n , m , c ;

int dcmp ( double x ) {
	return ( x > eps ) - ( x < -eps ) ;
}

int deal ( int r ) {
	For ( i , 1 , n ) {
		p2[i] = Point ( p[i].x * Cos[r] - p[i].y * Sin[r] , p[i].x * Sin[r] + p[i].y * Cos[r] ) ;
		//printf ( "%.5f %.5f\n" , p2[i].x , p2[i].y ) ;
	}

	For ( i , 1 , n ) {
		bool flag = 0 ;
		For ( j , 1 , n ) {
			if ( dcmp ( p2[i].x - p[j].x ) == 0 && dcmp ( p2[i].y - p[j].y ) == 0 ) {
				link[i] = j ;
				flag = 1 ;
				break ;
			}
		}
		if ( !flag ) return 0 ;
	}

	For ( i , 1 , m ) if ( G[link[L[i].p1]][link[L[i].p2]] == 0 ) return 0 ;
	++ Time ;
	int cnt = 0 ;
	For ( i , 1 , n ) if ( vis[i] != Time ) {
		++ cnt ;
		int x = i ;
		while ( vis[x] != Time ) {
			vis[x] = Time ;
			x = link[x] ;
		}
	}
	//printf ( "%d\n" , cnt ) ;
	return cnt ;
}

void solve () {
	double midx = 0 , midy = 0 ;

	clr ( G , 0 ) ;

	scanf ( "%d%d%d" , &n , &m , &c ) ;

	Pow[0] = 1 ;
	For ( i , 1 , n ) Pow[i] = Pow[i - 1] * c % mod ;

	For ( i , 1 , n ) {
		p[i].input () ;
		midx += p[i].x ;
		midy += p[i].y ;
	}
	midx /= n ;
	midy /= n ;
	For ( i , 1 , n ) p[i] = Point ( p[i].x - midx , p[i].y - midy ) ;

	For ( i , 1 , m ) {
		L[i].input () ;
		G[L[i].p1][L[i].p2] = G[L[i].p2][L[i].p1] = 1 ;
	}

	int ans = Pow[n] , cnt = 1 ;
	int rot1 = deal ( 1 ) ;
	int rot2 = deal ( 2 ) ;
	int rot3 = deal ( 3 ) ;
	if ( rot1 ) ans = ( ans + Pow[rot1] ) % mod , ++ cnt ;
	if ( rot2 ) ans = ( ans + Pow[rot2] ) % mod , ++ cnt ;
	if ( rot3 ) ans = ( ans + Pow[rot3] ) % mod , ++ cnt ;
	ans = ans * inv[cnt] % mod ;

	printf ( "%d\n" , ans ) ;
}

int main () {
	int T ;
	Time = 0 ;
	clr ( vis , 0 ) ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}


L:5081 Trie

AC自动机+树链剖分。


首先建立AC自动机,然后构建fail指针树。由于根据我们所求的就是在f(P)中所有Si的Di之和,而对于一个trait,其所影响的点正是fail指针树中其的子树。

然后我们对fail指针树进行树链剖分。

我们将影响转化成对链的操作,当产生一个新的trait时,首先我们排除重复的,留下深度尽量浅的(深的总能被替换成浅的),然后对该trait中Si的尾结点对应树在线段上的映射的位置pos[Si]上增加值dep[Si],用sum1记录,然后再在同一位置上增加值1,用sum2记录。则该trait对查询时的串Sj的贡献等于( dep[Sj] +1 ) * sum2[Sj] - sum1[Sj],刚好为trait中Si对查询中Sj的贡献。

对于查询我们排除深度浅的。同时由于查询中Si和Sj计算的有重复,且重复部分正好是Si和Sj的lca,于是我们再减去lca上的贡献。

本算法最主要的思想就是将既有对子树的操作又有对链的操作转化成全为对链的操作。


代码如下:


#include 
#include 
#include 
using namespace std ;

#pragma comment(linker, "/STACK:16777216")
#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 ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define root 1 , 1 , n
#define rt o , l , r
#define mid ( ( l + r ) >> 1 )

typedef long long LL ;

const int MAXN = 100005 ;
const int MAXE = 100005 ;

struct ac_automation {
	int next[MAXN][26] ;
	int fail[MAXN] ;
	int ac_root ;
	int Q[MAXN] , head , tail ;

	inline void clear () {
		clr ( next , -1 ) ;
		ac_root = 1 ;
	}

	inline void link ( int x , int y , char c ) {
		next[x][c - 'a'] = y ;
	}

	inline void build () {
		head = tail = 0 ;
		fail[ac_root] = ac_root ;
		rep ( i , 0 , 26 ) {
			if ( ~next[ac_root][i] ) {
				fail[next[ac_root][i]] = ac_root ;
				Q[tail ++] = next[ac_root][i] ;
			} else next[ac_root][i] = ac_root ;
		}
		while ( head != tail ) {
			int now = Q[head ++] ;
			rep ( i , 0 , 26 ) {
				if ( ~next[now][i] ) {
					fail[next[now][i]] = next[fail[now]][i] ;
					Q[tail ++] = next[now][i] ;
				} else next[now][i] = next[fail[now]][i] ;
			}
		}
	}
} ;

struct Edge {
	int v , n ;
	Edge () {}
	Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;

struct Seg {
	int st , ed , x ;
	Seg () {}
	Seg ( int st , int ed , int x ) : st ( st ) , ed ( ed ) , x ( x ) {}
	bool operator < ( const Seg& a ) const {
		return st < a.st ;
	}
} ;

ac_automation ac ;
Edge E[MAXE] ;
Seg S[MAXN] ;
int H[MAXN] , cntE ;
int top[MAXN] ;
int siz[MAXN] ;
int pre[MAXN] ;
int son[MAXN] ;
int pos[MAXN] ;
int dep[MAXN] ;
int in[MAXN] , ou[MAXN] , dfs_clock ;
LL sum1[MAXN << 2] ;
int sum2[MAXN << 2] ;
int tree_idx ;
int n , m ;

void clear () {
	cntE = 0 ;
	siz[0] = 0 ;
	dep[1] = 1 ;
	pre[1] = 0 ;
	tree_idx = 0 ;
	dfs_clock = 0 ;
	clr ( H , -1 ) ;
	clr ( sum1 , 0 ) ;
	clr ( sum2 , 0 ) ;
}

void addedge ( int u , int v ) {
	E[cntE] = Edge ( v , H[u] ) ;
	H[u] = cntE ++ ;
}

void dfs ( int u ) {
	in[u] = ++ dfs_clock ;
	siz[u] = 1 ;
	son[u] = 0 ;
	for ( int i = H[u] ; ~i ; i = E[i].n ) {
		int v = E[i].v ;
		dep[v] = dep[u] + 1 ;
		pre[v] = u ;
		dfs ( v ) ;
		siz[u] += siz[v] ;
		if ( siz[v] > siz[son[u]] ) son[u] = v ;
	}
	ou[u] = dfs_clock ;
}

void rebuild ( int u , int top_element ) {
	top[u] = top_element ;
	pos[u] = ++ tree_idx ;
	if ( son[u] ) rebuild ( son[u] , top_element ) ;
	for ( int i = H[u] ; ~i ; i = E[i].n ) {
		int v = E[i].v ;
		if ( son[u] != v ) rebuild ( v , v ) ;
	}
}

void update ( int x , int v , int o , int l , int r ) {
	if ( l == r ) {
		sum1[o] += v ;
		++ sum2[o] ;
		return ;
	}
	int m = mid ;
	if ( x <= m ) update ( x , v , lson ) ;
	else update ( x , v , rson ) ;
	sum1[o] = sum1[ls] + sum1[rs] ;
	sum2[o] = sum2[ls] + sum2[rs] ;
}

LL query1 ( int L , int R , int o , int l , int r ) {
	if ( L <= l && r <= R ) return sum1[o] ;
	//pushdown ( rt ) ;
	int m = mid ;
	if ( R <= m ) return query1 ( L , R , lson ) ;
	if ( m <  L ) return query1 ( L , R , rson ) ;
	return query1 ( L , R , lson ) + query1 ( L , R , rson ) ;
}

int query2 ( int L , int R , int o , int l , int r ) {
	if ( L <= l && r <= R ) return sum2[o] ;
	//pushdown ( rt ) ;
	int m = mid ;
	if ( R <= m ) return query2 ( L , R , lson ) ;
	if ( m <  L ) return query2 ( L , R , rson ) ;
	return query2 ( L , R , lson ) + query2 ( L , R , rson ) ;
}

LL Query ( int x , int y ) {
	LL depth = dep[x] > dep[y] ? dep[x] : dep[y] ;
	LL ans1 = 0 , ans2 = 0 ;
	while ( top[x] != top[y] ) {
		if ( dep[top[x]] < dep[top[y]] ) swap ( x , y ) ;
		ans1 += query1 ( pos[top[x]] , pos[x] , root ) ;
		ans2 += query2 ( pos[top[x]] , pos[x] , root ) ;
		x = pre[top[x]] ;
	}
	//printf ( "%d %d\n" , ans1 , ans2 ) ;
	if ( dep[x] > dep[y] ) swap ( x , y ) ;
	ans1 += query1 ( pos[x] , pos[y] , root ) ;
	ans2 += query2 ( pos[x] , pos[y] , root ) ;
	//printf ( "%d %d\n" , ans1 , ans2 ) ;
	return ( 1 + depth ) * ans2 - ans1 ;//important
}

int lca ( int x , int y ) {
	while ( top[x] != top[y] ) {
		if ( dep[top[x]] < dep[top[y]] ) swap ( x , y ) ;
		x = pre[top[x]] ;
	}
	if ( dep[x] > dep[y] ) swap ( x , y ) ;
	return x ;
}

void solve () {
	int op , k , x ;
	char c[2] ;
	ac.clear () ;
	clear () ;
	scanf ( "%d" , &n ) ;
	For ( i , 2 , n ) {
		scanf ( "%d%s" , &x , c ) ;
		ac.link ( x , i , c[0] ) ;
	}
	ac.build () ;
	For ( i , 2 , n ) {
		addedge ( ac.fail[i] , i ) ;
		//printf ( "%d->%d\n" , ac.fail[i] , i ) ;
	}
	dfs ( 1 ) ;
	rebuild ( 1 , 1 ) ;
	//For ( i , 1 , n ) printf ( "pos[%d] = %d\n" , i , pos[i] ) ;
	scanf ( "%d" , &m ) ;
	while ( m -- ) {
		scanf ( "%d%d" , &op , &k ) ;
		if ( op == 1 ) {
			rep ( i , 0 , k ) {
				scanf ( "%d" , &x ) ;
				S[i] = Seg ( in[x] , ou[x] , x ) ;
			}
			sort ( S , S + k ) ;
			int cur = 0 ;
			rep ( i , 1 , k ) {//remove child node
				if ( S[cur].st <= S[i].st && S[i].ed <= S[cur].ed ) S[i].x = 0 ;
				else cur = i ;
			}
			rep ( i , 0 , k ) if ( S[i].x ) {
				update ( pos[S[i].x] , dep[S[i].x] , root ) ;
			}
		} else {
			LL ans = 0 ;
			rep ( i , 0 , k ) {
				scanf ( "%d" , &x ) ;
				S[i] = Seg ( in[x] , ou[x] , x ) ;
			}
			sort ( S , S + k ) ;
			rep ( i , 1 , k ) {//remove ancestor node
				if ( S[i - 1].st <= S[i].st && S[i].ed <= S[i - 1].ed ) S[i - 1].x = 0 ;
			}
			int cur = -1 ;
			rep ( i , 0 , k ) {
				if ( S[i].x ) {
					ans += Query ( 1 , S[i].x ) ;
					if ( ~cur ) ans -= Query ( 1 , lca ( S[cur].x , S[i].x ) ) ;
					cur = i ;
				}
			}
			printf ( "%I64d\n" , ans ) ;
		}
	}
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}

本题还有一种稍有不同的方法:

AC自动机+线段树。

本质和上面的差不多,不过是将既有对子树的操作又有对链的操作转化成对子树的操作。

具体还需自行体会。


代码如下:


#include 
#include 
#include 
using namespace std ;

#pragma comment(linker, "/STACK:16777216")
#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 ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define root 1 , 1 , n
#define rt o , l , r
#define mid ( ( l + r ) >> 1 )

typedef long long LL ;

const int MAXN = 100005 ;
const int MAXE = 100005 ;

struct ac_automation {
	int next[MAXN][26] ;
	int fail[MAXN] ;
	int ac_root ;
	int Q[MAXN] , head , tail ;

	inline void clear () {
		clr ( next , -1 ) ;
		ac_root = 1 ;
	}

	inline void link ( int x , int y , char c ) {
		next[x][c - 'a'] = y ;
	}

	inline void build () {
		head = tail = 0 ;
		fail[ac_root] = ac_root ;
		rep ( i , 0 , 26 ) {
			if ( ~next[ac_root][i] ) {
				fail[next[ac_root][i]] = ac_root ;
				Q[tail ++] = next[ac_root][i] ;
			} else next[ac_root][i] = ac_root ;
		}
		while ( head != tail ) {
			int now = Q[head ++] ;
			rep ( i , 0 , 26 ) {
				if ( ~next[now][i] ) {
					fail[next[now][i]] = next[fail[now]][i] ;
					Q[tail ++] = next[now][i] ;
				} else next[now][i] = next[fail[now]][i] ;
			}
		}
	}
} ;

struct Edge {
	int v , n ;
	Edge () {}
	Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;

struct Seg {
	int st , ed , x ;
	Seg () {}
	Seg ( int st , int ed , int x ) : st ( st ) , ed ( ed ) , x ( x ) {}
	bool operator < ( const Seg& a ) const {
		return st < a.st ;
	}
} ;

ac_automation ac ;
Edge E[MAXE] ;
Seg S[MAXN] ;
int H[MAXN] , cntE ;
int top[MAXN] ;
int siz[MAXN] ;
int pre[MAXN] ;
int son[MAXN] ;
int pos[MAXN] ;
int dep[MAXN] ;
int in[MAXN] , ou[MAXN] , dfs_clock ;
LL add1[MAXN << 2] ;
LL sum1[MAXN << 2] ;
int add2[MAXN << 2] ;
int sum2[MAXN << 2] ;
int tree_idx ;
int n , m ;

void clear () {
	cntE = 0 ;
	siz[0] = 0 ;
	dep[1] = 1 ;
	pre[1] = 0 ;
	tree_idx = 0 ;
	dfs_clock = 0 ;
	clr ( H , -1 ) ;
	clr ( add1 , 0 ) ;
	clr ( add2 , 0 ) ;
	//clr ( sum1 , 0 ) ;
	//clr ( sum2 , 0 ) ;
}

void addedge ( int u , int v ) {
	E[cntE].v = v ;
	E[cntE].n = H[u] ;
	H[u] = cntE ++ ;
}

void dfs ( int u ) {
	siz[u] = 1 ;
	son[u] = 0 ;
	in[u] = ++ dfs_clock ;
	for ( int i = H[u] ; ~i ; i = E[i].n ) {
		int v = E[i].v ;
		dep[v] = dep[u] + 1 ;
		pre[v] = u ;
		dfs ( v ) ;
		siz[u] += siz[v] ;
		if ( siz[v] > siz[son[u]] ) son[u] = v ;
	}
	ou[u] = dfs_clock ;
}

void rebuild ( int u , int top_element ) {
	top[u] = top_element ;
	pos[u] = ++ tree_idx ;
	if ( son[u] ) rebuild ( son[u] , top_element ) ;
	for ( int i = H[u] ; ~i ; i = E[i].n ) {
		int v = E[i].v ;
		if ( son[u] != v ) rebuild ( v , v ) ;
	}
}

void pushdown ( int o ) {
	if ( add1[o] ) {
		add1[ls] += add1[o] ;
		add1[rs] += add1[o] ;
		add1[o] = 0 ;
	}
	if ( add2[o] ) {
		add2[ls] += add2[o] ;
		add2[rs] += add2[o] ;
		add2[o] = 0 ;
	}
}

void update ( int L , int R , int v , int o , int l , int r ) {
	if ( L <= l && r <= R ) {
		add1[o] += v ;
		add2[o] += 1 ;
		return ;
	}
	pushdown ( o ) ;
	int m = mid ;
	if ( L <= m ) update ( L , R , v , lson ) ;
	if ( m <  R ) update ( L , R , v , rson ) ;
}

LL query ( int x , LL depth , int o , int l , int r ) {
	if ( l == r ) return add2[o] * ( depth + 1 ) - add1[o] ;
	pushdown ( o ) ;
	int m = mid ;
	if ( x <= m ) return query ( x , depth , lson ) ;
	else return query ( x , depth , rson ) ;
}

int lca ( int x , int y ) {
	while ( top[x] != top[y] ) {
		if ( dep[top[x]] < dep[top[y]] ) swap ( x , y ) ;
		x = pre[top[x]] ;
	}
	if ( dep[x] > dep[y] ) swap ( x , y ) ;
	return x ;
}

void solve () {
	int op , k , x ;
	char c[2] ;
	ac.clear () ;
	clear () ;
	scanf ( "%d" , &n ) ;
	For ( i , 2 , n ) {
		scanf ( "%d%s" , &x , c ) ;
		ac.link ( x , i , c[0] ) ;
	}
	ac.build () ;
	For ( i , 2 , n ) addedge ( ac.fail[i] , i ) ;
	dfs ( 1 ) ;
	rebuild ( 1 , 1 ) ;
	scanf ( "%d" , &m ) ;
	while ( m -- ) {
		scanf ( "%d%d" , &op , &k ) ;
		if ( op == 1 ) {
			rep ( i , 0 , k ) {
				scanf ( "%d" , &x ) ;
				S[i] = Seg ( in[x] , ou[x] , x ) ;
			}
			sort ( S , S + k ) ;
			int cur = 0 ;
			rep ( i , 1 , k ) {//remove child node
				if ( S[cur].st <= S[i].st && S[i].ed <= S[cur].ed ) S[i].x = 0 ;
				else cur = i ;
			}
			rep ( i , 0 , k ) if ( S[i].x ) update ( S[i].st , S[i].ed , dep[S[i].x] , root ) ;
		} else {
			LL ans = 0 ;
			rep ( i , 0 , k ) {
				scanf ( "%d" , &x ) ;
				S[i] = Seg ( in[x] , ou[x] , x ) ;
			}
			sort ( S , S + k ) ;
			rep ( i , 1 , k ) {//remove ancestor node
				if ( S[i - 1].st <= S[i].st && S[i].ed <= S[i - 1].ed ) S[i - 1].x = 0 ;
			}
			int cur = -1 ;
			rep ( i , 0 , k ) {
				if ( S[i].x ) {
					ans += query ( S[i].st , dep[S[i].x] , root ) ;
					if ( ~cur ) {
						x = lca ( S[cur].x , S[i].x ) ;
						ans -= query ( in[x] , dep[x] , root ) ;
					}
					cur = i ;
				}
			}
			printf ( "%I64d\n" , ans ) ;
		}
	}
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) solve () ;
	return 0 ;
}


你可能感兴趣的:(题解,数学,DP,AC自动机【Trie图】,树链剖分,线段树,hdu)