【ACdream】Andrew Stankevich's Contest (4)

A:1235 Unique Attack

本题即求最小割是否唯一。

假设最小割唯一,那么沿着残余网络从源点到达的最远的边一定是与汇点出发沿着残余网络到达的最远的边是相同的。所以我们只要找到有这么一条边从源点出发能到达但是从汇点出发到达不了或者从汇点出发能到达但是从源点出发到达不了的边即可。大致思想是从源点以及汇点出发沿着残余网络分别做两次dfs,将从源点出发能到达的点标记为1,将从汇点出发能到达的点标记为2,则最后如果存在一条边的两个端点中只有一个被标记,则说明最小割不唯一。


代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
//#include <cmath>
#include <queue>
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 travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1)
#define root 1 , 1 , n
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
 
const int MAXN = 805 ;
const int MAXE = 400005 ;
const int INF = 0x3f3f3f3f ;
 
struct Edge {
	int v , c , n ;
	Edge () {}
	Edge ( int v , int c , int n ) : v ( v ) , c ( c ) , n ( n ) {}
} E[MAXE] ;
 
struct Seg {
	int u , v ;
	Seg () {}
	Seg ( int u , int v ) : u ( u ) , v ( v ) {}
} seg[MAXE] ;
 
int H[MAXN] , cntE ;
int d[MAXN] , cur[MAXN] , pre[MAXN] , cnt[MAXN] ;
int Q[MAXN] , head , tail ;
int vis[MAXN] ;
int s , t , nv ;
int flow ;
int n , m ;
 
void clear () {
	cntE = 0 ;
	clr ( H , -1 ) ;
	clr ( vis , 0 ) ;
}
 
void addedge ( int u , int v , int c ) {
	E[cntE] = Edge ( v , c , H[u] ) ;
	H[u] = cntE ++ ;
	E[cntE] = Edge ( u , c , H[v] ) ;
	H[v] = cntE ++ ;
}
 
void rev_bfs () {
	head = tail = 0 ;
	clr ( d , -1 ) ;
	clr ( cnt , 0 ) ;
	Q[tail ++] = t ;
	cnt[0] = 1 ;
	while ( head != tail ) {
		int u = Q[head ++] ;
		for ( int i = H[u] ; ~i ; i = E[i].n ) {
			int v = E[i].v ;
			if ( d[v] == -1 ) {
				d[v] = d[u] + 1 ;
				Q[tail ++] = v ;
				cnt[d[v]] ++ ;
			}
		}
	}
}
 
int ISAP () {
	cpy ( cur , H ) ;
	rev_bfs () ;
	flow = 0 ;
	int u = pre[s] = s , i , f , pos , minv ;
	while ( d[s] < nv ) {
		if ( u == t ) {
			f = INF ;
			for ( i = s ; i != t ; i = E[cur[i]].v ) {
				if ( f > E[cur[i]].c ) {
					f = E[cur[i]].c ;
					pos = i ;
				}
			}
			for ( i = s ; i != t ; i = E[cur[i]].v ) {
				E[cur[i]].c -= f ;
				E[cur[i] ^ 1].c += f ;
			}
			flow += f ;
			u = pos ;
		}
		for ( i = cur[u] ; ~i ; i = E[i].n ) if ( E[i].c && d[u] == d[E[i].v] + 1 ) break ;
		if ( ~i ) {
			cur[u] = i ;
			pre[E[i].v] = u ;
			u = E[i].v ;
		} else {
			if ( 0 == -- cnt[d[u]] ) break ;
			for ( minv = nv , i = H[u] ; ~i ; i = E[i].n ) {
				int v = E[i].v ;
				if ( E[i].c && minv > d[v] ) {
					minv = d[v] ;
					cur[u] = i ;
				}
			}
			d[u] = minv + 1 ;
			cnt[d[u]] ++ ;
			u = pre[u] ;
		}
	}
	return flow ;
}
 
void sdfs ( int u ) {
	vis[u] = 1 ;
	for ( int i = H[u] ; ~i ; i = E[i].n ) {
		if ( vis[E[i].v] != 1 && E[i].c ) sdfs ( E[i].v ) ;
	}
}
 
void tdfs ( int u ) {
	vis[u] = 2 ;
	for ( int i = H[u] ; ~i ; i = E[i].n ) {
		if ( vis[E[i].v] != 2 && E[i ^ 1].c ) tdfs ( E[i].v ) ;
	}
}
 
void solve () {
	int u , v , c ;
	clear () ;
	nv = n + 1 ;
	rep ( i , 0 , m ) {
		scanf ( "%d%d%d" , &u , &v , &c ) ;
		addedge ( u , v , c ) ;
		seg[i] = Seg ( u , v ) ;
	}
	ISAP () ;
	sdfs ( s ) ;
	tdfs ( t ) ;
	int flag = 0 ;
	rep ( i , 0 , m ) {
		u = seg[i].u ;
		v = seg[i].v ;
		if ( vis[u] == 1 && !vis[v] ) flag = 1 ;
		if ( vis[v] == 1 && !vis[u] ) flag = 1 ;
		if ( vis[u] == 2 && !vis[v] ) flag = 1 ;
		if ( vis[v] == 2 && !vis[u] ) flag = 1 ;
	}
	if ( flag ) printf ( "AMBIGUOUS\n" ) ;
	else printf ( "UNIQUE\n" ) ;
}
 
int main () {
	while ( ~scanf ( "%d%d%d%d" , &n , &m , &s , &t ) && ( n || m || s || t ) ) solve () ;
	return 0 ;
}

B:1236 Burning Bridges

tarjan双连通求桥。。


代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
//#include <cmath>
#include <queue>
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 travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1)
#define root 1 , 1 , n
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
 
const int MAXN = 200005 ;
const int MAXE = 400005 ;
 
struct Edge {
	int v , idx ;
	Edge* next ;
} E[MAXE] , *H[MAXN] , *edge ;
 
int dfn[MAXN] , low[MAXN] , dfs_clock ;
int S[MAXE] , top ;
int n , m ;
 
void clear () {
	top = 0 ;
	edge = E ;
	clr ( H , 0 ) ;
	clr ( dfn , 0 ) ;
	dfs_clock = 0 ;
}
 
void addedge ( int u , int v , int idx ) {
	edge->v = v ;
	edge->idx = idx ;
	edge->next = H[u] ;
	H[u] = edge ++ ;
}
 
void tarjan ( int u , int fa = 0 ) {
	dfn[u] = low[u] = ++ dfs_clock ;
	int flag = 1 ;
	travel ( e , H , u ) {
		int v = e->v ;
		if ( v == fa && flag ) {
			flag = 0 ;
			continue ;
		}
		if ( !dfn[v] ) {
			tarjan ( v , u ) ;
			low[u] = min ( low[u] , low[v] ) ;
			if ( low[v] > dfn[u] ) S[top ++] = e->idx ;
		} else low[u] = min ( low[u] , dfn[v] ) ;
	}
}
 
void solve () {
	int u , v ;
	clear () ;
	For ( i , 1 , m ) {
		scanf ( "%d%d" , &u , &v ) ;
		addedge ( u , v , i ) ;
		addedge ( v , u , i ) ;
	}
	For ( i , 1 , n ) if ( !dfn[i] ) tarjan ( i ) ;
	sort ( S , S + top ) ;
	printf ( "%d\n" , top ) ;
	int flag = 0 ;
	rep ( i , 0 , top ) {
		if ( flag ) printf ( " " ) ;
		flag = 1 ;
		printf ( "%d" , S[i] ) ;
	}
	printf ( "\n" ) ;
}
 
int main () {
	while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
	return 0 ;
}

E:1239 DVD

题目分析:dp方程很简单。。设dp[i][j][k]表示匹配到第i天正处在第j个region且切换频道k次后看片最多的次数。

每一个年份,我们贪心处理:对相邻的两年i,i+1,假设第i年正处在第j个位置,第i+1年处在第l个位置,则我们枚举子集,使得子集包括j和l,同时我们需要预处理出第i年第j个region内的片数,将子集包括的区域内的片数全部累加得到和sum,同时记录切换频道的次数cnt,如果j = l且cnt>0则该答案必定不是最优的,因为我们总可以在下一年切换回来来使得答案不比原来的差。如果j != l那么如果k+cnt>5则continue,因为不符合条件,否则dp[i][l][k+cnt] = max { dp[i][l][k+cnt],dp[i-1][j][k] }。同时我们记录前驱的时候用结构体表示,记录信息有前一年的年份(可以不记录,不过记录了方便输出路径时的终止判断),前一年正处在的region,前一年使用的次数,今年看片的region集合(二进制表示)。输出路径递归输出,注意细节即可。


#include <cstdio>
#include <cstring>
#include <algorithm>
//#include <cmath>
#include <queue>
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 travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )

const int MAXN = 2010 ;
const int MAXE = 2005 ;

struct Edge {
	int v ;
	Edge* next ;
} E[MAXE] , *H[MAXN][6] , *edge ;

struct Pre {
	int i , j , k , S ;
	Pre () {}
	Pre ( int i , int j , int k , int S ) : i ( i ) , j ( j ) , k ( k ) , S ( S ) {}
} pre[MAXN][6][6] ;

char film[MAXN][105] ;
int dp[MAXN][6][6] ;
int num[MAXN][6] ;
int n ;

void clear () {
	edge = E ;
	clr ( H , 0 ) ;
	clr ( num , 0 ) ;
	clr ( dp , 0 ) ;
}

void addedge ( int i , int j , int k ) {
	edge->v = k ;
	edge->next = H[i][j] ;
	H[i][j] = edge ++ ;
}

void read ( int i ) {
	char c = 0 ;
	int year , region ;
	int l = 0 ;
	while ( ( c = getchar () ) != '"' ) ;
	film[i][l ++] = c ;
	while ( ( c = getchar () ) != '"' ) film[i][l ++] = c ;
	film[i][l ++] = c ;
	film[i][l] = 0 ;
	scanf ( "%d%d" , &year , &region ) ;
	++ num[year][region - 1] ;
	addedge ( year , region - 1 , i ) ;
}

void print ( int year , int region , int used , int S ) {
	Pre& p = pre[year][region][used] ;
	if ( p.i ) print ( p.i , p.j , p.k , pre[p.i][p.j][p.k].S ) ;
	if ( !p.i ) {
		rep ( i , 0 , 5 ) if ( S & ( 1 << i ) ) if ( i != region ) {
			for ( Edge* e = H[year][i] ; e ; e = e->next ) {
				printf ( "%s\n" , film[e->v] ) ;
			}
		}
		for ( Edge* e = H[year][region] ; e ; e = e->next ) {
			printf ( "%s\n" , film[e->v] ) ;
		}
		return ;
	}
	for ( Edge* e = H[year][p.j] ; e ; e = e->next ) {
		printf ( "%s\n" , film[e->v] ) ;
	}
	rep ( i , 0 , 5 ) if ( S & ( 1 << i ) ) if ( i != p.j && i != region ) {
		for ( Edge* e = H[year][i] ; e ; e = e->next ) {
			printf ( "%s\n" , film[e->v] ) ;
		}
	}
	if ( p.j != region ) {
		for ( Edge* e = H[year][region] ; e ; e = e->next ) {
			printf ( "%s\n" , film[e->v] ) ;
		}
	}
}

void solve () {
	clear () ;
	For ( i , 1 , n ) read ( i ) ;
	rep ( i , 0 , 5 ) {
		rep ( S , 1 , 32 ) if ( S & ( 1 << i ) ) {
			int cnt = 0 , sum = 0 ;
			rep ( x , 0 , 5 ) if ( S & ( 1 << x ) ) {
				++ cnt ;
				sum += num[1870][x] ;
			}
			if ( sum > dp[1870][i][cnt - 1] ) {
				dp[1870][i][cnt - 1] = sum ;
				pre[1870][i][cnt - 1] = Pre ( 0 , 0 , 0 , S ) ;
			}
		}
	}
	For ( i , 1871 , 2004 ) {
		rep ( j , 0 , 5 ) {
			For ( k , 0 , 5 ) {
				rep ( l , 0 , 5 ) {
					rep ( S , 1 , 32 ) if ( ( S & ( 1 << l ) ) && ( S & ( 1 << j ) ) ) {
						int cnt = 0 , sum = 0 ;
						rep ( x , 0 , 5 ) if ( S & ( 1 << x ) ) {
							if ( x != j ) ++ cnt ;
							sum += num[i][x] ;
						}
						if ( j == l && cnt ) continue ;
						//上一层是j这一层如果跨region且终点又是j,则一定不是最优
						
						if ( cnt + k > 5 ) continue ;
						if ( dp[i - 1][j][k] + sum > dp[i][l][k + cnt] ) {
							dp[i][l][k + cnt] = dp[i - 1][j][k] + sum ;
							pre[i][l][k + cnt] = Pre ( i - 1 , j , k , S ) ;
						}
					}
				}
			}
		}
	}
	int ans = 0 , region , used ;
	rep ( j , 0 , 5 ) For ( k , 0 , 5 ) {
		if ( ans < dp[2004][j][k] ) {
			ans = dp[2004][j][k] ;
			region = j ;
			used = k ;
		}
	}
	printf ( "%d\n" , ans ) ;
	print ( 2004 , region , used , pre[2004][region][used].S ) ;
}

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


F:1240 Think Positive

答案就是1的个数减去-1的个数。。如果小于0则输出0。


G:1241 Ranking

对着题意模拟就行了。。注意细节问题就好。

提一点:如果有两个队伍同一场比赛中同时处于rank1,则如果下一个队伍为rank3。PMi为该场AC最多的队伍AC的题目数。


代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
//#include <cmath>
#include <queue>
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 travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )

const int MAXN = 105 ;

struct Team {
	int idx ;
	int join_cnt ;
	double T ;
	bool operator < ( const Team& a ) const {
		return T > a.T ;
	}
} team[MAXN] ;

struct Node {
	int ac_cnt ;
	int time ;
	int idx ;
	int join ;
	bool operator < ( const Node& a ) const {
		if ( join != a.join ) return join > a.join ;
		if ( ac_cnt != a.ac_cnt ) return ac_cnt > a.ac_cnt ;
		return time < a.time ;
	}
	bool operator > ( const Node& a ) const {
		return a < *this ;
	}
	bool operator != ( const Node& a ) const {
		return a < *this || *this < a ;
	}
} node[MAXN] ;

char name[MAXN][MAXN] ;
int name_len[MAXN] , max_len ;
int AC[MAXN][26] , problem_solve[26] ;
int penalty_time[MAXN][26] ;
int rank[MAXN] ;
int n , m ;

void solve () {
	int op ;//提交数
	int member ;
	char problem[2] , status[2] ;
	int time ;
	max_len = 0 ;
	clr ( team , 0 ) ;
	getchar () ;
	For ( i , 1 , n ) {
		gets ( name[i] ) ;
		team[i].idx = i ;
		name_len[i] = strlen ( name[i] ) ;
		if ( name_len[i] > max_len ) max_len = name_len[i] ;
	}
	scanf ( "%d" , &m ) ;
	For ( i , 1 , m ) {
		int K , A , B , PM = 0 ;
		int problem_num ;
		clr ( AC , 0 ) ;
		clr ( node , 0 ) ;
		clr ( penalty_time , 0 ) ;
		clr ( problem_solve , 0 ) ;
		scanf ( "%d" , &K ) ;
		For ( j , 1 , K ) {
			scanf ( "%d" , &member ) ;
			++ team[member].join_cnt ;
			node[member].join = 1 ;
			node[member].idx = member ;
		}
		scanf ( "%d%d" , &problem_num , &op ) ;
		while ( op -- ) {
			scanf ( "%d%s%d%s" , &member , problem , &time , status ) ;
			char c = problem[0] - 'A' ;
			if ( AC[member][c] ) continue ;
			if ( status[0] == '+' ) {
				AC[member][c] = 1 ;
				node[member].ac_cnt ++ ;
				problem_solve[c] = 1 ;
				node[member].time += time + penalty_time[member][c] ;
			} else penalty_time[member][c] += 20 ;
		}
		sort ( node + 1 , node + n + 1 ) ;
		int cnt = 1 , RANK = 1 ;
		rank[node[cnt].idx] = 1 ;
		For ( j , 2 , K ) {
			++ RANK ;
			if ( node[j] != node[cnt] ) {
				rank[node[j].idx] = RANK ;
				cnt = j ;
			} else rank[node[j].idx] = rank[node[cnt].idx] ;
		}
		For ( k , 1 , K ) {
			int tmp = 0 ;
			rep ( j , 0 , problem_num ) if ( AC[node[k].idx][j] ) ++ tmp ;
			if ( tmp > PM ) PM = tmp ;
		}
		A = 2 * K - 2 ;
		B = K - 2 ;
		if ( PM ) For ( j , 1 , K ) {
			team[node[j].idx].T += ( double ) node[j].ac_cnt * A / PM / ( rank[node[j].idx] + B ) ;
		}
	}
	For ( i , 1 , n ) if ( team[i].join_cnt ) team[i].T /= team[i].join_cnt ;
	sort ( team + 1 , team + n + 1 ) ;
	For ( i , 1 , n ) {
		int idx = team[i].idx , tmp = max_len - name_len[idx] ;
		printf ( "%s" , name[idx] ) ;
		For ( j , 0 , tmp ) printf ( " " ) ;
		printf ( "%.4f\n" , team[i].T ) ;
	}
}

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

H:1242 Driving Straight

题目要求的是在走最短路的前提下,如果能直走就直走,否则便从左转和右转中符合条件的一种中随意选择走即可,并不要求走出转弯次数最少。从终点反向BFS一次然后正向寻找路径即可。


代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
//#include <cmath>
#include <queue>
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 travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )

const int MAXN = 405 ;

char G[MAXN << 1][MAXN << 1] ;
int E[MAXN * MAXN][4] ;
int vis[MAXN * MAXN] ;
int Q[MAXN * MAXN] , head , tail ;
int d[MAXN * MAXN] ;
int s , t ;
int n , m ;
//up 0
//left 1
//down 2
//right 3

void bfs () {
	head = tail = 0 ;
	clr ( vis , 0 ) ;
	Q[tail ++] = t ;
	vis[t] = 1 ;
	d[t] = 0 ;
	while ( head != tail ) {
		int u = Q[head ++] ;
		rep ( i , 0 , 4 ) {
			int v = E[u][i] ;
			if ( v == -1 ) continue ;
			if ( vis[v] ) continue ;
			vis[v] = 1 ;
			d[v] = d[u] + 1 ;
			Q[tail ++] = v ;
		}
	}
	//printf ( "%d\n" , d[s] ) ;
}

void go () {
	int u , way ;
	if ( d[s] == d[E[s][0]] + 1 ) printf ( "N\n" ) , u = E[s][0] , way = 0 ;
	else printf ( "E\n" ) , u = E[s][3] , way = 3 ;
	while ( u != t ) {
		if ( ~E[u][way] && d[u] == d[E[u][way]] + 1 ) {
			printf ( "F" ) ;
			u = E[u][way] ;
		} else {
			if ( ~E[u][( way + 1 ) % 4] && d[u] == d[E[u][( way + 1 ) % 4]] + 1 ) {
				printf ( "L" ) ;
				way = ( way + 1 ) % 4 ;
				u = E[u][way] ;
			} else if ( ~E[u][( way + 3 ) % 4] && d[u] == d[E[u][( way + 3 ) % 4]] + 1 ) {
				printf ( "R" ) ;
				way = ( way + 3 ) % 4 ;
				u = E[u][way] ;
			}
		}
	}
	printf ( "\n" ) ;
}

void solve () {
	clr ( E , -1 ) ;
	int N = 2 * n - 1 ;
	int M = 2 * m - 1 ;
	s = ( n - 1 ) * m ;
	t = m - 1 ;
	getchar () ;
	rep ( i , 0 , N ) gets ( G[i] ) ;
	for ( int i = 0 ; i < 2 * n ; i += 2 ) {
		for ( int j = 0 ; j < 2 * m ; j += 2 ) {
			int x = i / 2 ;
			int y = j / 2 ;
			int u = x * m + y ;
			int v1 = x * m + y + 1 ;
			int v2 = x * m + y + m ;
			if ( G[i][j + 1] == '-' && y < m - 1 ) {
				E[u][3] = v1 , E[v1][1] = u ;
				//printf ( "%d-%d\n" , u , v1 ) ;
			}
			if ( G[i + 1][j] == '|' && x < n - 1 ) {
				E[u][2] = v2 , E[v2][0] = u ;
				//printf ( "%d-%d\n" , u , v2 ) ;
			}
		}
	}
	bfs () ;
	go () ;
}

int main () {
	while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
	return 0 ;
}


-------------------分割线----------------

今天太惨了。。一个小时三题然后就再也没出过题了。。。。各种题目读错真是要哭了T U T



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值