赛中只搞出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: [email protected] **********************************************/ #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: [email protected] **********************************************/ #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: [email protected] **********************************************/ #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( ); } }