BestCoder#27

BestCoder#27 2015/1/24

前面的基本思路来自hdu的官网解答
1001 Jump and Jump...

首先算出每个人的成绩,然后sort一下就好了,考虑
  
  
   
   n
  
  的范围只有2或者3,只要用if+swap也是可行的。

1002 Taking Bus

题目大意:
给你一个线路上的站点个数和站点之间的距离,根据发车的个数确定发车站点,再根据上车人的站点和目的地计算出时间,车从左往右然后折返着开,一个单位距离用一秒

输入输出样例:

 
 
输入样例
1 (T-组数)
7 3(7为n,表示站点数;3为m,乘车人数)
2 3 4 3 4 5 (七个站之间的距离)
1 7(第一个人,从站1到站7,出发站为1)
4 5(第二个人,从站1到站7,出发站为2)
5 4
(始发站是这么算的:第i个人的话,始发站为(i-1)mod n +1)
输出样例
21
10
28
提示:
对于第一个人, 公交在站点1出发, 然后这个人在时刻0上车。21秒之后,公交到达站点7。
对于第二个人,公交在站点2出发。7秒之后,公交到达站点4,这个人上车。之后又过了3秒,公交到达站点5.总共用了10秒。
对于第三个人,公交在站点3出发。7秒之后,公交到达站点5,这个人上车。之后过了9秒,公交达到站点7,然后转向开往站点0。之后经过12秒,公交达到站点4。因此总共需要28秒时间。

简单的分类讨论,设 s,x,y 分别表示公交的始发站,起点和终点。大概有这样几种情况:1. sx<y , 2. x<s<y ,3. x<ys , 4. sy<x , 5. y<s<x , 6. y<xs 分别写出公式即可。答案应该会超过int,注意要用long long。

解题思路:
首先是分情况讨论,其次呢,关于怎么计算一段路程的长度,这个呢,我们很容易联想到线段树的干活,其实除了线段树,还有一个简单方法,就是开一个distance[]数组,distance[i]=前i个点的距离,那么i-j的距离不就是distance[j]-distance[i]吗

经验总结:
注意取值的大小,我就是因为一开始没有注意long long的问题所以wrong answer了!

#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#define maxn 100003
using namespace std;
long long int vals ;
long long int val[maxn] ;
struct line
{
int left,right ;
long long int sum ;
}a[maxn*4];
void buil ( int l , int r , int n )
{
    int mid ;
    a[n].left = l ;
    a[n].right = r ;
    if( l == r-1 ) {
        a[n].sum = val[l] ;
        return ;
    }
    mid = ( l + r ) / 2 ;
    buil( l , mid , 2*n ) ;
    buil( mid , r , 2*n+1 ) ;
    a[n].sum = a[2*n].sum + a[2*n+1].sum ;
}
long long int getval ( int i , int j ,int start )
{
    int mid ;long long int sum = 0 ;
    if( i == j ){
        return 0;
    }
    if( a[start].left == i && a[start].right== j ){
       return a[start].sum ;
    }
    mid = ( a[start].left + a[start].right )/2 ;
    if( mid >= j ){
      sum += getval( i , j , 2*start ) ;
    }
    else{
        if( mid <= i ){
            sum += getval( i , j , 2*start+1 ) ;
        }
        else{
            sum += getval( i , mid , 2*start ) ;
            sum += getval( mid , j , 2*start+1 ) ;
        }
    }
    return sum ;
}


int main()
{
    int T,n,m;
    int s1,s2,ss;
    int ju1,ju2;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        for(int i = 1 ; i
        
        
       
       
      
      
     
     
    
    


1003 Matching on Array

题目大意:
给你一个Alice串,很长的整数串,给你一堆Bob串,很多个整数串,问Alice串里有几个Bob串出现,这里出现定义为存在一个Alice的连续子串,其前后比例与Bob串相同,比如2 4 6 8 的Alice串,1 2 4 的Bob串,那么2 4 6 就与1 2 4 匹配

输入输出样例:

     
     
输入样例
2(组数T)
4 1(Alice串长,Bob串个数)
2 4 8 16(alice 串)
2 1 2(Bob串长   Bob串)
5 3
2 4 2 4 6
3 1 2 1
1 5
2 16 8
输出样例
3(2 4   4 8   8 16 共3个)
7

首先我们考虑 m=1 的情况。给定两个数组 A={a1,a2,,an} B={b1,b2,,bk} ,问 B A 中出现了几次。令 ci=ai+1ai,1i<n ,同样令 di=bi+1bi,1i<k ,那么上述问题可以转化为 ci di 的模式匹配问题,这个正确性显然,也没有什么好证明的。于是对于 m=1 的情况只有用个kmp即可搞定。现在考虑 m>1 的情况,我们考虑用ac自动机来做。考虑到字符集并不是普通的数字,而是一个分数,我们不放搞个分数类,然后用map存转移边。用 m 个模式串(Bob的序列)建好自动机之后,把文本串(Alice的序列)在自动机上跑一遍即可统计出答案。

解题思路:
首先,我们得有个问题转化,不是求一个子串C,满足 C与Bob子串成比例吗,那么我们就有:c2/b2 = c1/b1;这其实就转成:c2/c1 = b2/b1; c3/c2 = b3/b2 那么Bob子串就可以转化成比例串了
最后其实就是一个ac自动机变形题,不过这个变形还挺巧妙的,我们把储存的字符串变成比例,而这样的改变也会使得我们固定的26的空间大小发生改变,为此,我们利用map来解决
1. 利用map的关键字,我们可以不固定每个比例值的分支个数和数值
2. 关键字的选取:比如比例为 b1 b2 那么 b2/b1的值就记为b2/gcd(b1,b2)*100001+b1,可以证明,这个关键字独一无二

经验总结:
此题很棒啊,不过由于是ac自动机的变形,所以很多东西都要转化过来,比如map数组mp,mp[i]相当于第i个节点,map[i][j]相当于第i个节点比例为j的那个子节点,其值为子节点的序号

【本代码,来自http://blog.csdn.net/u013368721/article/details/43118531的分享】

    
    
#include 
       
       
        
        
#include 
        
        
#include 
         
         
          
          
#include 
          
          
           
           
#include 
           
           
             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 clr( a , x ) memset ( a , x , sizeof a ) const int MAXN = 1000005 ; LL ans ; int gcd ( int a , int b ) { return b ? gcd ( b , a % b ) : a ; } struct AC_automation { map < int , int > mp[MAXN] ; map < int , int > :: iterator it ; int fail[MAXN] ; int end[MAXN] ; int Q[MAXN] , head , tail ; int root ; int p ; int newnode () { mp[p].clear () ; end[p] = 0 ; return p ++ ; } void init () { p = 0 ; ans = 0 ; root = newnode () ; } void insert ( int s[] , int n ) { int now = root ; rep ( i , 1 , n ) { int g = gcd ( s[i] , s[i + 1] ) ; int t = s[i] / g * 10001 + s[i + 1] / g ; if ( !mp[now].count ( t ) ) mp[now][t] = newnode () ; now = mp[now][t] ; } ++ end[now] ; } void build () { head = tail = 0 ; fail[root] = root ; for ( it = mp[root].begin () ; it != mp[root].end () ; ++ it ) { fail[it->second] = root ; Q[tail ++] = it->second ; } while ( head != tail ) { int now = Q[head ++] ; end[now] += end[fail[now]] ; for ( it = mp[now].begin () ; it != mp[now].end () ; ++ it ) { int x = fail[now] , flag = 0 ; while ( x != root ) { if ( mp[x].count ( it->first ) ) { fail[it->second] = mp[x][it->first] ; flag = 1 ; break ; } x = fail[x] ; } if ( x == root && mp[x].count ( it->first ) ) { fail[it->second] = mp[x][it->first] ; flag = 1 ; } if ( !flag ) fail[it->second] = root ; Q[tail ++] = it->second ; } } } void query ( int s[] , int n ) { int now = root ; rep ( i , 1 , n ) { int g = gcd ( s[i] , s[i + 1] ) ; int t = s[i] / g * 10001 + s[i + 1] / g ; int x = now , flag = 0 ; while ( x != root ) { if ( mp[x].count ( t ) ) { now = mp[x][t] ; flag = 1 ; break ; } x = fail[x] ; } if ( x == root && mp[x].count ( t ) ) { now = mp[x][t] ; flag = 1 ; } if ( !flag ) now = root ; ans += end[now] ; } } } ; AC_automation ac ; int s[100005] , p[300005] ; int n , m , q ; void solve () { ans = 0 ; ac.init () ; scanf ( "%d%d" , &n , &q ) ; For ( i , 1 , n ) scanf ( "%d" , &s[i] ) ; while ( q -- ) { scanf ( "%d" , &m ) ; For ( i , 1 , m ) scanf ( "%d" , &p[i] ) ; if ( m > 1 ) ac.insert ( p , m ) ; else ans += n ; } ac.build () ; ac.query ( s , n ) ; printf ( "%I64d\n" , ans ) ; } int main () { int T ; scanf ( "%d" , &T ) ; while ( T -- ) solve () ; return 0 ; } 
           
          
          
         
         
       
       

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值