赛中只搞出ABC,C想的还有点久,没时间搞D了(虽然是推了很久都没推出来),蒟蒻的上分之旅。
A. Visiting a Friend
题意:
你的初始位置为0,给你n和m,m表示你的目的地,n表示可乘坐工具的个数。接下来n行,每行一个x y,表示在位置x处有一个工具,你可以坐着它到位置y(你可以中途跳车,比如样例1中,0->2,2->4,在2->4过程中跳车,在位置3坐上3->5,可以抵达m=5的位置)。问你能不能用这个工具到达m。
思路:
保证n组工具覆盖的长度是0-m即可。
贪心:把工具存结构体然后排个序,按x从小到大和y从小到大排好,初始化当前位置为0,遍历工具,考虑:如果当前位置大于等于工具的
x,就把当前位置赋值成y,否则肯定无法到达当前x处,即无法借助这种工具到达m。
AC代码:
/**********************************************
Author: StupidTurtle
Date: 2017.12.19
Email: 31601359@stu.zucc.edu.cn
**********************************************/
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll ;
int n , m , ans ;
struct node{
int x , y ;
};
node a[105] ;
bool cmp ( const node &a , const node &b ){
if ( a.x != b.x ) return a.x < b.x ;
return a.y < b.y ;
}
int main(void){
ans = 0 ;
cin >> n >> m ;
for ( int i = 0 ; i < n ; i ++ ){
cin >> a[i].x >> a[i].y ;
}
sort ( a , a + n , cmp );
for ( int i = 0 ; i < n ; i ++ ){
if ( a[i].x <= ans ){
ans = max ( ans , a[i].y );
} else {
break ;
}
}
if ( ans == m ) cout << "YES" << endl ;
else cout << "NO" << endl ;
return 0 ;
}
B. Coloring a Tree
题意:
第一行给你一个n,表示n个点;第二行输入n-1个数字,p2,p3,...,pn,分别表示pi的父节点(认为1是树的根);第三行输入n个数字,表示c1,c2,...,cn,表示ci节点想被染上的颜色,c1,c2,...,cn,初值均为0。将某个点染色后,它的子树也会变成和它一样的颜色。问将整棵树染成目标颜色的树最少需要多少步。
思路:
因为对一个节点染色会影响其子树,所以先染根节点再向下染色。
dfs:因为ci是[1,n]而c1初值为0,所以根节点必被染色,所以初始化cnt为1,dfs的带两个参数,分别为当前点下标和父节点的颜色,如果父节点颜色和当前点的目标颜色不同,则cnt++,遍历每个子节点。
AC代码:
/**********************************************
Author: StupidTurtle
Date: 2017.12.19
Email: 31601359@stu.zucc.edu.cn
**********************************************/
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll ;
int n , prt , ex[10005] , cnt ;
vector<int> tree[10005] ;
void dfs ( int v0 , int col ){
if ( ex[v0] != col ){
cnt ++ ;
}
int l = tree[v0].size() ;
for ( int i = 0 ; i < l ; i ++ ){
dfs ( tree[v0][i] , ex[v0] );
}
}
int main(void){
cin >> n ;
for ( int i = 2 ; i <= n ; i ++ ){
cin >> prt ;
tree[prt].push_back(i) ;
}
for ( int i = 1 ; i <= n ; i ++ ){
cin >> ex[i] ;
}
cnt = 1 ;
dfs ( 1 , ex[1] );
cout << cnt << endl ;
return 0 ;
}
C. Hashing Trees
题意:
给你一个n,表示一颗树的高度,第二行n+1个数,分别表示树的第0层,第1层,...,第n层有多少个节点,问你这样的树是否存在两颗不同构的,如果没有则输出"perfect",否则输出"ambiguous",然后接下来两行其中的任意两颗。
(同构:两棵树如果可以通过交换子树可以变成一样的,则这两棵树同构。)
思路:
做过B题之后做这道题可以注意道:这道题对树的描述也仅仅是一颗有根树且根为1,并不是一颗二叉树(可以从B题的Sample2看出),那我们要思考的就是对于一颗非二叉树,怎么样做到让他唯一。由Sample1想到,如果全是1则必定perfect。在Sample2中1 2 2可以得到两棵非同构的树,那么如果是1 2 1呢?第2层的一个结点可以挂在第1层的任意一个子结点上,所以也是perfect的。由此推广:只要没有连续两个非1的节点数,则该树是perfect的。接下来考虑输出问题,只要输出其中一对就可以了,所以在第一次出现连续非1的层数时,在第一颗树中将后一层的一个挂在前一层的第一个节点上,其余的挂在前一层的第二个结点上,在第二颗树中将后一层的所有节点都挂在前一层的第一个节点上。对于其他情况,直接将后一层的节点全都挂在前一层的第一个节点上即可。
AC代码:
/**********************************************
Author: StupidTurtle
Date: 2017.12.19
Email: 31601359@stu.zucc.edu.cn
**********************************************/
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll ;
int n , a[200005] , crt , cnt , l , pf ;
bool flag , first ;
int main(void){
flag = true ;
cin >> n ;
for ( int i = 0 ; i <= n ; i ++ ){
cin >> a[i] ;
}
crt = a[0] ;
for ( int i = 1 ; i <= n ; i ++ ){
if ( a[i] != 1 && crt != 1 ){
flag = false ;
break ;
} else {
crt = a[i] ;
}
}
if ( flag == true ){
cout << "perfect" << endl ;
} else {
cout << "ambiguous" << endl ;
first = false ;
cout << "0" ;
crt = a[0] ;
l = 1 ;
cnt = 1 ;
for ( int i = 1 ; i <= n ; i ++ ){
pf = l ;
if ( crt != 1 && a[i] != 1 ){
if ( first == false ){
for ( int j = 0 ; j < a[i] ; j ++ ){
cnt ++ ;
if ( j == 0 ){
cout << " " << pf ;
l = cnt ;
}
else cout << " " << pf+1 ;
}
flag = true ;
} else {
for ( int j = 0 ; j < a[i] ; j ++ ){
cout << " " << pf ;
cnt ++ ;
if ( j == 0 ) l = cnt ;
}
}
} else {
for ( int j = 0 ; j < a[i] ; j ++ ){
cout << " " << pf ;
cnt ++ ;
if ( j == 0 ) l = cnt ;
}
crt = a[i] ;
}
}
cout << endl ;
cout << "0" ;
crt = a[0] ;
l = 1 ;
cnt = 1 ;
for ( int i = 1 ; i <= n ; i ++ ){
pf = l ;
for ( int j = 0 ; j < a[i] ; j ++ ){
cout << " " << pf ;
cnt ++ ;
if ( j == 0 ) l = cnt ;
}
crt = a[i] ;
}
cout << endl ;
}
return 0 ;
}
D. GCD of Polynomials
题意:
规定A(X),B(X),R(X),D(X)分别是一个多项式,定义A(X)=B(X)*D(X)+R(X),且R(X)的最高次的次数要比B(X)的最高次的次数要小。这样子定义出GCD(A(X),B(X)) = R(X)。这个描述定义了多项式的gcd。然后输入一个n,表示进行n次gcd(A(X),B(X))后B(X)的结果为0。要求分四行输出进行第一次gcd(A(X),B(X))之前:A(X)的最高次的次数;次数由低到高输出A(X)每一项的系数;B(X)的最高次的次数;次数由低到高输出B(X)每一项的系数。
思路:
赛中没有想到,赛后得知:如果进行第n次gcd的结果是A(X),B(X),那么进行第n-1次的结果就是A(X)*X±B(X),A(X)。尝试多推几项:
0----------------1----------------0
1----------------x----------------1
2-------------x^2+1------------x
3--------------x^3------------x^2+1
4---------x^4+x^2+1---------x^3
转换成二进制后的结果:
0--------------000001------------000000
1--------------000010------------000001
2--------------000101------------000010
3--------------001000------------000101
4--------------010101------------001000
可以发现从第一行开始,每一行的B(X)变成了上一行的A(x)(显而易见),而每一行的A(X)变成了这一行的B(X)左移一位后再异或上一行的B(X)。
由于n是[1,150],1<<150在C++存不下(我用int在Sample31TLE,用longlong在Sample63TLE),所以在我的方法下用了java
AC代码:
import java.util.Scanner ;
import java.math.* ;
public class Main {
public static void main ( String[] args ) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt() ;
BigInteger l = BigInteger.valueOf(1) , r = BigInteger.valueOf(0) , mid ;
for ( int i = 2 ; i <= n + 1 ; i ++ ) {
mid = r ;
r = l ;
l = l.shiftLeft(1) ;
l = l.xor(mid) ;
}
System.out.println( n );
for ( int i = 0 ; i < n + 1 ; i ++ ) {
if ( i != 0 ) System.out.print( " " );
System.out.print( l.and(BigInteger.valueOf(1) ) );
l = l.shiftRight(1) ;
}
System.out.println( );
System.out.println( n - 1 );
for ( int i = 0 ; i < n ; i ++ ) {
if ( i != 0 ) System.out.print( " " );
System.out.print( r.and(BigInteger.valueOf(1) ) );
r = r.shiftRight(1) ;
}
System.out.println( );
}
}