现在的位置: 首页 > 综合 > 正文

【CodeForces】Round #270 A,B,C,D题解

2017年10月16日 ⁄ 综合 ⁄ 共 3635字 ⁄ 字号 评论关闭

传送门:Codeforces Round #270

A:Design Tutorial: Learn from Math

奇数输出9,n-9;偶数输出4,n-4。

B:Design Tutorial: Learn from Life

排个序,ans = 第1大+第K+1大+第2K+1大+...,可以保证这样一定是最优的,因为下一趟的最大时间被尽量减小了。

C:Design Tutorial: Make It Nondeterministic

对所有编号为i的人两个节点i<<1,i<<1|1,分别代表名和姓。

将题目要求的序列读取到num[]数组里。

如果num[i-1]的名字典序小于num[i]的名,则建边(num[i-1]<< 1,num[i]<<1)。

其他边同理。

最后把序列第一个人的名和姓放进队列bfs。

如果bfs完最后一个人的名或者姓被遍历到,则说明存在一种选择方案使得序列合法,输出YES。

否则输出NO。

D:Design Tutorial: Inverse the Problem

这题我考虑的简单了。。。比赛的时候用了n*n*logn+n*n*logn*logn的算法过的。。。

我是这么想的:首先求一次最小生成树(我用的kruskal,但prim效果更好,因为是完全图),然后以最小生成树的树边建图。接下来我是用树链剖分来做的,暴力判断给的矩阵中的值是否和树链剖分得到的值相同。。。。。。然后只需要注意矩阵的对称以及自己到自己应该是0,其他就没什么了。。。过是没问题的。

现在我讲一下这道题的正确姿势,赛后才发现可以这么做。。

首先还是求最小生成树,用prim,这部分复杂度O(n*n)。

用最小生成树得到的树边建树。

然后枚举以每个点作为根,dfs得到这个点到其余所有点的距离,然后和给的矩阵比较看是否相同。

每次dfs复杂度O(n),总复杂度也是O(n*n)。

如果dfs得到的距离和矩阵给的完全符合则输出YES,否则NO。

算法的总复杂度O(n*n)。

由于实在是懒得写第二个算法的程序了,有看明白的可以自己实现下,反正我是不想动了。。

就贴一下我树链剖分的代码吧。。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
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 lson ls , l , m
#define rson rs , m + 1 , r
#define root 1 , 1 , n
#define mid ( ( l + r ) >> 1 )
#define rt o , l , r

const int MAXN = 3005 ;
const int MAXE = 10005 ;
const int MAXM = 2000005 ;

struct Line {
	int u , v , val ;
	int flag ;
	Line () {}
	Line ( int u , int v , int val ) : u ( u ) , v ( v ) , val ( val ) , flag ( 0 ) {}
	bool operator < ( const Line& a ) const {
		return val < a.val ;
	}
} line[MAXM] ;

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

LL sum[MAXN << 2] ;
int G[MAXN][MAXN] ;
int siz[MAXN] ;
int pos[MAXN] ;
int pre[MAXN] ;
int top[MAXN] ;
int son[MAXN] ;
int val[MAXN] ;
int dep[MAXN] ;
int tree_idx ;
int p[MAXN] ;
int cnt ;
int n ;

void clear () {
	cnt = 0 ;
	edge = E ;
	clr ( H , 0 ) ;
	tree_idx = 0 ;
	siz[0] = 0 ;
	rep ( i , 0 , MAXN ) p[i] = i ;
}

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

int find ( int x ) {
	return p[x] == x ? x : ( p[x] = find ( p[x] ) ) ;
}

bool kruskal () {
	int count = 1 ;
	sort ( line , line + cnt ) ;
	rep ( i , 0 , cnt ) {
		int x = find ( line[i].u ) ;
		int y = find ( line[i].v ) ;
		if ( x != y ) {
			p[x] = y ;
			line[i].flag = 1 ;
			addedge ( line[i].u , line[i].v ) ;
			addedge ( line[i].v , line[i].u ) ;
			if ( ++ count == n ) return 1 ;
		}
	}
	return 0 ;
}

void dfs ( int u ) {
	siz[u] = 1 ;
	son[u] = 0 ;
	travel ( e , H , u ) {
		int v = e -> v ;
		if ( v != pre[u] ) {
			pre[v] = u ;
			dep[v] = dep[u] + 1 ;
			dfs ( v ) ;
			siz[u] += siz[v] ;
			if ( siz[v] > siz[son[u]] ) son[u] = v ;
		}
	}
}

void rewrite ( int u , int top_element ) {
	top[u] = top_element ;
	pos[u] = ++ tree_idx ;
	if ( son[u] ) rewrite ( son[u] , top_element ) ;
	travel ( e , H , u ) {
		int v = e -> v ;
		if ( v != pre[u] && v != son[u] ) rewrite ( v , v ) ;
	}
}

void pushup ( int o ) {
	sum[o] = sum[ls] + sum[rs] ;
}

void build ( int o , int l , int r ) {
	if ( l == r ) {
		sum[o] = val[l] ;
		return ;
	}
	int m = mid ;
	build ( lson ) ;
	build ( rson ) ;
	pushup ( o ) ;
}

LL sub_query ( int L , int R , int o , int l , int r ) {
	if ( L <= l && r <= R ) return sum[o] ;
	int m = mid ;
	if ( R <= m ) return sub_query ( L , R , lson ) ;
	if ( m <  L ) return sub_query ( L , R , rson ) ;
	return sub_query ( L , R , lson ) + sub_query ( L , R , rson ) ;
}

LL query ( int x , int y ) {
	LL ans = 0 ;
	while ( top[x] != top[y] ) {
		if ( dep[top[x]] < dep[top[y]] ) swap ( x , y ) ;
		ans += sub_query ( pos[top[x]] , pos[x] , root ) ;
		x = pre[top[x]] ;
	}
	if ( x == y ) return ans ;
	if ( dep[x] > dep[y] ) swap ( x , y ) ;
	ans += sub_query ( pos[x] + 1 , pos[y] , root ) ;
	return ans ;
}

void solve () {
	clear () ;
	bool ok = 1 ;
	For ( i , 1 , n ) {
		For ( j , 1 , n ) {
			scanf ( "%d" , &G[i][j] ) ;
			if ( i == j && G[i][j] ) ok = 0 ;
			if ( i > j && G[i][j] != G[j][i] ) ok = 0 ;
			if ( !ok ) continue ;
			if ( i < j && G[i][j] ) line[cnt ++] = Line ( i , j , G[i][j] ) ;
		}
	}
	if ( !ok ) {
		printf ( "NO\n" ) ;
		return ;
	}
	if ( n == 1 ) {
		printf ( "YES\n" ) ;
		return ;
	}
	if ( !kruskal () ) {
		printf ( "NO\n" ) ;
		return ;
	}
	dfs ( 1 ) ;
	rewrite ( 1 , 1 ) ;
	rep ( i , 0 , cnt ) {
		if ( line[i].flag ) {
			int x = line[i].u ;
			int y = line[i].v ;
			if ( dep[x] > dep[y] ) val[pos[x]] = line[i].val ;
			else val[pos[y]] = line[i].val ;
		}
	}
	build ( root ) ;
	For ( i , 1 , n ) For ( j , i + 1 , n ) {
		if ( G[i][j] != query ( i , j ) ) ok = 0 ;
		if ( ok == 0 ) break ;
	}
	if ( ok ) printf ( "YES\n" ) ;
	else printf ( "NO\n" ) ;
}

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

抱歉!评论已关闭.