NBUT 2014 校赛 (Minecraft专场)

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 10005
int 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 数组对应的下标 , 初始值为 0
int 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 用于加快计算速度

// 预处理RMQ
void 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 1e16
int 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 100005

long 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 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值