NBUT 2014 校赛 (Minecraft专场)
来源:互联网 发布:钢联数据3.0 编辑:程序博客网 时间:2024/04/19 07:57
A 题 Minecraft Server Bug
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1552
题意 : 给你一串W,L串,你需要找一对W,L,要求W在L之前,能找到多少对。
思路 : 直接遍历字符串 , 用一个变量记录之前出现过多少W了,遇到L就加上这个数量就行。
#include <stdio.h>int main(){int n ;while( scanf( "%d" , &n ) != EOF ) {long long ans = 0 ;long long cntW = 0 ;char str[3] ;while( n -- ) {scanf( "%s" , str ) ;if( str[0] == 'W' ) cntW ++ ;else ans += cntW ;}printf( "%lld\n" , ans ) ;}return 0 ;}
B 题 Beautiful Walls
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1553
题意: 给你一堆unit( 就是墙吧 ? 这游戏没玩过 ) , 每个unit有高度 , 你需要选择一个区间 [ a , b ] , 使这个区间中的unit没有高度相同的。问你有多少种选择的方法。
思路: 枚举区间的终点 , 对于每个终点 , 我们只要知道起点最前面可以是多少就行了。那么如何确定这个最前面的起点,很显然,如果当前枚举的这个高度之前已经出现过了,那么起点一定在上次出现这个高度的unit之后 , 那么我们规定 pre[i] 为和第i个unit高度相同的unit的下标。那么对于以一个终点,那么他的最前面的起点就是max( pre[j] | j < =i ) + 1 了。为什么取pre[j]的最大值? 确定pre[i]的话 , 可以用map或者hash记录下就可以了。
#include <stdio.h>#include <string.h>#include <algorithm>#include <map>using namespace std ;map<int,int> last ;int main(){int n ;while( scanf( "%d" , &n ) != EOF ) {last.clear() ;long long ans = 0 ;int lll = 0 ;for( int i = 1 ; i <= n ; i ++ ) {int a ;scanf( "%d" , &a ) ;int ll = last[a] ; last[a] = i ;lll = max( lll , ll ) ;ans += i - lll ;}printf( "%lld\n" , ans ) ;}return 0 ;}
C 题 Lord of Minecraft LCA
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1554
题意: 给你一棵树,问从a点跑到b点,会不会经过和a点同样深度的点,如果b点的深度和a点相同,也算经过。
思路: 其实就是判断a和b是不是祖孙关系,如果是的话,肯定不会经过,不会的话,比较深度就可以了
那么判断a和b是不是祖孙关系,通常就是条件反射求LCA。
出题人给了另外一个思路,就是直接dfs的时候,计算进入每个顶点和离开每个顶点的时间戳,如果a和b是祖孙关系,那么a和b的时间戳区间必然是一个区间包含另一个区间。
下面的代码的话,是用LCA的。
#include <stdio.h>#include <string.h>#include <math.h>#include <stdlib.h>#include <map>#include <string>#include <algorithm>using namespace std;#define MAXN 10005#define MAXM 10005int n , m , q ;map<string,int> mp ;struct Graph{ int head[MAXN] ; int Index ; struct Edge{ int to , nex , val ; Edge(){} Edge( int _to , int _nex , int _val ):to(_to),nex(_nex),val(_val){} }edge[MAXM*2] ; void init(){ memset(head,-1,sizeof(head)); Index = 0 ; } void add( int from , int to , int val = 0 ){ edge[Index] = Edge( to , head[from] , val ) ; head[from] = Index ++ ; }}gra;int degree[MAXN] ;int E[MAXN*2] ; // dfs的下标int R[MAXN] ; // 每个时间戳所对应的节点int H[MAXN*2] ; // E中对应节点的深度bool vis[MAXN] ; // 某个节点是否被访问int id ; // E 数组对应的下标 , 初始值为 0int dis[MAXN] ; // 点u距离根节点的距离(边有边权的情况下)int Index ;void dfs( int u , int d , int dist ){ // dfs 时的节点u , 离根节点的距离d E[++id] = u ; R[u] = id ; H[id] = d ; vis[u] = true ; dis[u] = dist ; for( int i = gra.head[u] ; ~i ; i = gra.edge[i].nex ){ int ch = gra.edge[i].to ; if( !vis[ch] ){ dfs( ch , d + 1 , dist + gra.edge[i].val ) ; E[++id] = u ; H[id] = d ; } }}int stmin[MAXN*2][21] ; // 注意!!存的是最小值的下标,不是最小值int d[21] ; // d[i] = 1 << i 用于加快计算速度// 预处理RMQvoid InitRMQ(){ d[0]=1; for( int i=1;i<21;i++ )d[i] = d[i-1]<<1 ; for( int i=1;i<=id;i++ ) stmin[i][0] = i ; for( int j=1;d[j]<=id;j++ ) for( int i=1;i+d[j]-1<=id;i++ ) stmin[i][j] = H[stmin[i][j-1]]<H[stmin[i+d[j-1]][j-1]]?stmin[i][j-1]:stmin[i+d[j-1]][j-1] ; }// 构完树之后直接调用记得可 , 可以解决存在森林的情况void solve(){ id = 0 ; memset(vis,false,sizeof(vis)); //dfs(1,0,0);for( int i = 1 ; i <= Index ; i ++ ) {if( degree[i] == 0 ) {dfs( i , 0 , 0 ) ;break;}}/*for( int i=1;i<=Index;i++ ){ if(!vis[i]){ while( true ) ; } }*/ InitRMQ();}// 若LCA == -1 则说明点a和点b在不同的连通块int LCA( int a , int b ){ // 返回LCA(a,b) int l = R[a] , r = R[b] ; if( l > r )swap( l , r ) ; int k = int( log(double(r-l+1))/log(2.0) ); int lca = H[stmin[l][k]]<H[stmin[r-d[k]+1][k]]?stmin[l][k]:stmin[r-d[k]+1][k] ; return E[lca] ;}int main(){int casn = 0 ;while( scanf("%d%d",&n,&q)!=EOF ){casn ++ ;//if( casn == 3 ) while( true ) ; gra.init();mp.clear() ;Index = 0 ;memset( degree , 0 , sizeof(degree) ) ; for( int i=1;i<=n;i++ ){char name1[105] , name2[105] ;scanf( "%s%s" , name1 , name2 ) ;int ida = mp[name1], idb = mp[name2] ;if( ida == 0 ) {ida = mp[name1] = ++Index ;}if( idb == 0 ) {idb = mp[name2] = ++Index ; } gra.add(ida,idb,1); gra.add(idb,ida,1);degree[ida] ++ ; } solve();int ans = 0 ; while(q--){char name1[105] , name2[105] ;scanf( "%s%s" , name1 , name2 ) ;int ida = mp[name1] , idb = mp[name2] ;//if( ida == 0 || idb == 0 ) {//while( true ) ;//} int lca = LCA(ida,idb);if( lca == ida || lca == idb ) {continue ;}if( dis[ida] <= dis[idb] ) ans ++ ; //printf("%d\n",dis[a]+dis[b]-2*dis[lca]); }printf( "%d\n" , ans ) ; } return 0 ;}
D 题 The Sum of F(x) and G(x)
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1555
题意: 就是模拟两个多项式相加
思路: 数据量很小,怎么搞都行
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int c[30] ;int main(){int n , m ;while( scanf( "%d%d" , &n , &m ) != EOF ) {memset( c , 0 , sizeof(c) ) ;while( n -- ) {int a , b ;scanf( "%d%d" , &a , &b ) ;c[b+15] += a ; }while( m -- ) {int a , b ;scanf( "%d%d" , &a , &b ) ;c[b+15] += a ;}for( int i = 25 ; i >= 0 ; i -- ) {if( c[i] ) {printf( "%d %d\n" , c[i] , i - 15 ) ;}}}return 0 ;}
E 题 Flandre at Minecraft
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1556
题意 : 等于给你个N*N的01串,你要通过最小的操作数把他变成全0串,你可以有三种操作,
1: 取反一个元素
2. 取前D*K个元素取反
3. 取后D*K个元素取反
D是题目给出的,并保证能被N*N整除,K是你任意选取的,并每次操作可以不一样。
求最小操作数。
思路 : 还没想出来 ... 想出来再补 ...
F 题 Team of Slime
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1557
题意: 给你一个1~N的排列,你要通过一些操作把这个排列变成1,2,...,N这样的有序排列。但是每次操作只能把某个数提到数列的最前面。问最小操作数
思路: 很明显,如果 a[i] > a[j] 并且 i < j , 那么如果最后要让他们变得有序,肯定是要有某次操作把a[j]提到前面去的。那么这样之后,所有小于a[j]都必须往前提一遍,也只需要提一遍(只要从a[j]-1,a[j]-2...,1这样的顺序往前提) , 就能让1...a[j]是有序的在前j个了。那么我们还可以想到,如果a[j]比较小,这样操作完,当有比他大的往前提的时候,这个操作就全白费了,所以我们很容易就可以想到,第一个往前提的就是满足上述关系的最大的一个a[j] ,并且最后答案就是a[j]。那么这样我们只要记录每个元素出现的位置就可以了。
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int pos[300005] ;int main(){int n; while( scanf( "%d" , &n ) != EOF ) {int a ; for( int i = 1 ;i <= n ;i ++ ) {scanf( "%d" , &ai ) ; pos[a] = i ;}int ans = 0 ;int Min = n ;for( int i = n ; i >= 1 ; i -- ) {if( pos[i] > Min ) {ans = i ;break;}else{Min = min( Min , pos[i] ) ;}}printf( "%d\n" , ans ) ;} return 0 ;}
G题 Racing Cheat 计算几何 + 最短路
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1558
题意 : 起点是( 0 , 0 ) , 终点是( n , n ) , 然后给你n条线段作为障碍物,求从起点到终点的最短路。
思路: 把起点终点还是线段的端点都处理到邻接矩阵中,两点之间没有线段阻拦的就连上权值为长度的边,跑遍flody就行了
#include <stdio.h>#include <string.h>#include <algorithm>#include <math.h>using namespace std ;#define POW(x) ((x)*(x))#define eps 1e-8#define zero(x) (((x)>0?(x):-(x))<eps)#define INF 1e16int n , m , peo ;struct Point{ double x, y ; Point(){} Point( double X ,double Y ):x(X),y(Y){}}p[305];struct Line{ Point a , b ; Line(){} Line(Point A , Point B):a(A),b(B){}}l[105];double Map[305][305] ;double Dist( Point a , Point b ){ return sqrt( POW(a.x-b.x) + POW(a.y-b.y));}double xmult(Point p1,Point p2,Point p0){ return(p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);}int opposite_side(Point p1,Point p2,Line l){ return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)<-eps;}int intersect_ex(Line u,Line v){ return opposite_side(u.a,u.b,v) && opposite_side(v.a,v.b,u);}int cntP ;int main(){ double ll ; while( scanf("%lf%d",&ll,&m) != EOF ){ cntP = 0 ;n = 2 ;p[++cntP] = Point( 0 , 0 ) ;p[++cntP] = Point( ll , ll ) ; for( int i=1;i<=m;i++ ){ double a , b , c , d ; scanf("%lf%lf%lf%lf",&a,&b,&c,&d); p[++cntP] = Point(a,b); p[++cntP] = Point(c,d); l[i] = Line(p[cntP-1],p[cntP]); } int N = n + 2 * m ; for( int i=1;i<=N;i++ ){ for( int j=1;j<=N;j++ ) Map[i][j] = INF ; Map[i][i]=0; } for( int i=1;i<=N;i++ ){ for( int j=i+1;j<=N;j++ ){ bool ok = true ; for( int k=1;k<=m;k++ ){ if(intersect_ex(Line(p[i],p[j]),l[k])){ ok = false ;break; } } if( ok ){ Map[i][j] = Map[j][i] = Dist(p[i],p[j]); } } } for( int k=1;k<=N;k++ ) for( int i=1;i<=N;i++ ) for( int j=1;j<=N;j++ ) if( i!=j && j!=k && i!=k && Map[i][k] + Map[k][j] < Map[i][j] ) Map[i][j] = Map[i][k] + Map[k][j] ; printf("%.2lf\n", Map[1][2] ); } return 0 ;}
H题 Jump to the Top of Mountain dfs
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1559
题意: 给你一张图,每个点都有高度,外围又一圈高度为0的地方,你可以选择外围任意的点为起点,你只能往四个方向走,并且你当前处于高度x的话,你不能走到高度大于x+1的格子,问你能不能走到最高峰。( PS : 貌似最高峰有多个 , 只要能走到其中一个就可以了 )
思路: 图很小,只要从最高点往外dfs,看看能不能走到边界就行了
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int n , m ;int Max ;int mp[105][105] ;bool vis[105][105] ;int dir[4][2] = { 1 , 0 , -1 , 0 , 0 , 1 , 0 , -1 } ;bool ok ;void dfs( int x , int y ) {if( x < 0 || x > n || y < 0 || y > m ) {ok = true ;return ;}vis[x][y] = true ;for( int i = 0 ; i < 4 ; i ++ ) {int nex = x + dir[i][0] , ney = y + dir[i][1] ;if( vis[nex][ney] ) continue ;if( mp[nex][ney] + 1 >= mp[x][y] ) {dfs( nex , ney ) ;if( ok ) return ;}}}int main(){while( scanf( "%d%d" , &n , &m ) != EOF ) {Max = 0 ;memset( mp , 0 , sizeof(mp) ) ;for( int i = 1 ;i <= n ;i ++ ) {for( int j = 1 ; j <= m ; j ++ ) {scanf( "%d" , &mp[i][j] ) ;Max = max( Max , mp[i][j] ) ;}}memset( vis , false , sizeof(vis) ) ;ok = false ;for( int i = 1 ; i <= n && ok == false ; i ++ ) {for( int j = 1 ; j <= m && ok == false ; j ++ ) {if( mp[i][j] == Max ) {dfs( i , j ) ;}}}puts( ok?"YES":"NO") ;}return 0 ;}
I 题 Let Slimes Grow Up 线段树
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1560
题意 : 给你n个数,有两个操作,一个是区间[a,b]所有数加上c,另一个是查询[a,b]区间中的方差大小。
思路: 主要只要求解出 [a,b] 区间中 Σ( x ^ 2 ) 和 Σ ( x ) 就可以了。求Σ(x) 直接线段树区间更新,区间查询即可。Σ( x^2 ) 在更新的时候要通过( x + c ) ^ 2 = ( x ^ 2 + 2 * c * x + c ^2 ) 这个公式维护即可。
嘛...这题貌似因为是精度问题...一直过不了... 下面是代码。在OJ上AC不了
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 #define MAXN 100005long long sum1[MAXN<<2] , sum2[MAXN<<2] , lazy[MAXN<<2] ;void pushup( int rt ) {sum1[rt] = sum1[rt<<1] + sum1[rt<<1|1] ;sum2[rt] = sum2[rt<<1] + sum2[rt<<1|1] ;}void build( int l , int r , int rt ) {lazy[rt] = 0 ;if( l == r ) {scanf( "%lld" , &sum1[rt] ) ;sum2[rt] = sum1[rt] * sum1[rt] ;return ;}int m = ( l + r ) >> 1 ;build( lson ) ;build( rson ) ;pushup( rt ) ;}void pushdown( int rt , int len ) {lazy[rt<<1] += lazy[rt] ;lazy[rt<<1|1] += lazy[rt] ;sum2[rt<<1] += 2 * lazy[rt] * sum1[rt<<1] + lazy[rt] * lazy[rt] * ( len - len / 2 ) ;sum2[rt<<1|1] += 2 * lazy[rt] * sum1[rt<<1|1] + lazy[rt] * lazy[rt] * ( len / 2 ) ;sum1[rt<<1] += lazy[rt] * ( len - len / 2 ) ;sum1[rt<<1|1] += lazy[rt] * ( len / 2 ) ;lazy[rt] = 0 ;}void update( int l , int r , int rt , int L , int R , int add ) {if( L <= l && R >= r ) {sum2[rt] += ( r - l + 1 ) * add * add + 2 * add * sum1[rt] ;sum1[rt] += ( r - l + 1 ) * add ;lazy[rt] += add ;return ;}if( lazy[rt] ) {pushdown( rt , r - l + 1 ) ;}int m = ( l + r ) >> 1 ;if( L <= m ) {update( lson , L , R , add ) ;}if( R > m ) {update( rson , L , R , add ) ;}pushup( rt ) ;}long long query1( int l , int r , int rt , int L , int R ) {if( L <= l && R >= r ) {return sum1[rt] ;} if( lazy[rt] ) pushdown( rt , r - l + 1 ) ;int m = ( l + r ) >> 1 ;long long ans = 0 ;if( L <= m ) {ans += query1( lson , L , R ) ;}if( R > m ) {ans += query1( rson , L , R ) ;}return ans ;}long long query2( int l , int r , int rt , int L , int R ) {if( L <= l && R >= r ) {return sum2[rt] ;} if( lazy[rt] ) pushdown( rt , r - l + 1 ) ;int m = ( l + r ) >> 1 ;long long ans = 0 ;if( L <= m ) {ans += query2( lson , L , R ) ;}if( R > m ) {ans += query2( rson , L , R ) ;}return ans ;}int main(){int n , m ;long long s1 , s2 ;double ave , ans ;while( scanf( "%d%d" , &n , &m )!= EOF ) {build( 1 , n , 1 ) ;while( m -- ) {int op ;scanf( "%d" , &op ) ;if( op == 1 ) {int a , b , c ;scanf( "%d%d%d" , &a , &b , &c ) ;a ++ ; b ++ ; update( 1 , n , 1 , a , b , c ) ;}else if( op == 2 ) {int a , b ;scanf( "%d%d" , &a , &b ) ; a ++ ; b ++ ;s1 = query1( 1 , n , 1 , a , b ) ;s2 = query2( 1 , n , 1 , a , b ) ;ave = s1 * 1.0 / ( b - a + 1 ) ;ans = ( s2 - 2 * ave * s1 + ave * ave * ( b - a + 1 ) ) / ( b - a + 1 ) ;printf( "%lld\n" , (long long)ans ) ;}}}return 0 ;}
J题 Set Time
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1561
题意 : 就是计算s秒时候是处在第几天和第几个月第几年
思路: 嘛...我可耻的拖了zju的模板...
#include <stdio.h>#include <string.h>#include <algorithm>#include <map>#include <math.h>using namespace std ;int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};class Date {public: //判闰年 inline static bool isLeap(int year) { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; } int year, month, day; //判合法性 inline bool isLegal() const { if (month <= 0 || month > 12) { return false; } if (month == 2) { return day > 0 && day <= 28 + isLeap(year); } return day > 0 && day <= days[month - 1]; } //比较日期大小 inline int compareTo(const Date & other) const { if (year != other.year) { return year - other.year; } if (month != other.month) { return month - other.month; } return day - other.day; } //返回指定日期是星期几 0 (Sunday) ... 6 (Saturday) inline int toWeekday() const { int tm = month >= 3 ? (month - 2) : (month + 10); int ty = month >= 3 ? year : (year - 1); return (ty + ty / 4 - ty / 100 + ty / 400 + (int)(2.6 * tm - 0.2) + day) % 7; } //日期转天数偏移 inline int toInt() const { int ret = year * 365 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400; days[1] += isLeap(year); for (int i = 0; i < month - 1; ret += days[i++]); days[1] = 28; return ret + day; } //天数偏移转日期 inline void fromInt(int a) { year = a / 146097 * 400; for (a %= 146097; a >= 365 + isLeap(year); a -= 365 + isLeap(year), year++); days[1] += isLeap(year); for (month = 1; a >= days[month - 1]; a -= days[month - 1], month++); days[1] = 28; day = a + 1; }}start,finish;int main(){int n ;start.year = 1970 , start.month = 1 , start.day = 1 ;int start_shift = start.toInt () ;while( scanf( "%d" , &n ) != EOF ) {int day = n / ( 60 * 60 * 24 ) ;int finish_shift = start_shift + day ;finish.fromInt( finish_shift ) ;printf( "year: %d\n" , finish.year - 1970 + 1 ) ;printf( "month: %d\n" , ( finish.year - 1970 ) * 12 + finish.month ) ;printf( "day: %d\n" , day + 1 ) ;}return 0 ;}
K题 Brick Game 博弈
http://acm.nbut.edu.cn/Problem/view.xhtml?id=1562
题意: ... 题意在比赛两天后...才知道真正的题意 ... T。T读错了两遍...
题意是给你n堆石子,你有两种操作:
1. 可以选择1堆石头减少石头的个数
2.可以选择1堆石头个数为0的石堆,删除后面所有的石堆( 注意是删除,不是清零,清零石堆还是存在的 ,删除是石堆就不存在了 )
谁把最后一个石堆删掉,谁就赢。
举个例子 : 0000 这个应该是先手胜 ...
思路 : 直接根据PN状态记忆化搜索即可... 数据量不大
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int dp[1000005][6] ;int ten[9] ;int solve( int n , int len ) {if( dp[n][len-1] != -1 ) return dp[n][len-1] ;if( n < ten[len] ) {return dp[n][len] = 1 ;}int t = n ;for( int i = 1 ; i <= len ; i ++ ) {int tmp = t % 10 ; t /= 10 ;if( tmp > 0 ) {for( int j = 1 ; j <= tmp ; j ++ ) {if( solve( n - j * ten[i] , len ) == 0 ) {return dp[n][len-1] = 1 ;}}}else{if( solve( t , len - i ) == 0 ) {return dp[n][len-1] = 1 ;}}}return dp[n][len-1] = 0 ;}int main(){ten[1] = 1 ;for( int i = 2 ; i <= 7 ; i ++ ) ten[i] = ten[i-1] * 10 ;memset( dp , -1 , sizeof(dp) ) ;int n ;char str[15] ;while( scanf( "%s" , &str ) != EOF ) {int len = strlen( str ) ;sscanf( str , "%d" , &n ) ;if( n == 0 ) {puts( "Hahaha" ) ; continue ;}puts( solve( n , len ) ? "Hahaha":"Papapa" ) ;} return 0 ;}
- NBUT 2014 校赛 (Minecraft专场)
- NBUT Minecraft Server Bug
- NBUT 1552 Minecraft Server Bug
- NBUT 1554 Lord of Minecraft
- NBUT 1554 Lord of Minecraft
- Minecraft
- Minecraft
- NBUT
- NBUT
- NBUT
- NBUT
- NBUT
- 【NBUT
- NBUT
- NBUT
- NBUT
- NBUT
- NBUT
- 对vector等STL标准容器进行排序操作
- tomcat性能优化
- leetcode: Rotate Image
- Linux C 内存泄漏检测工具
- 最近老是精力不集中啊 一脑多用啊
- NBUT 2014 校赛 (Minecraft专场)
- 开启调试控制台
- 关于ffmpeg 的总结(一个linux 下 集 屏幕录像录音,音频视频转换,合并音频视频文件,格式转换于一身的命令)
- redis代码 发布订阅
- python 中包的安装
- Unity3D的断点调试功能
- C++ 中delete小记
- 新工作3周多了,进展呢、、、
- windows7系统笔记本设置成虚拟WiFi热点(即“无线路由器”)