8.11 hdu 多校第五场补题

◇◆丶佛笑我妖孽 提交于 2019-11-27 00:28:41

6629 string matching

题意:给定一个字符串s,求s与自身所有前缀暴力匹配所需匹配次数

分析:每个前缀匹配次数即自身长度,exkmp的next数组记录的就是与自身最大匹配前缀的长度,加一遍就行,注意边界

代码:

 

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>

#define fi first
#define se second
#define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
#define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
#define mem( a ,x ) memset( a ,x ,sizeof(a))
using namespace std;

typedef long long ll;
typedef pair<int ,int> pii;
typedef pair<ll ,ll> pll;
typedef pair<string ,int> psi;

char s[1005000];
int nxt[1005000];

void getnext( int l){
     int a=0 ,p=0;
     nxt[0]=l;
     for( int i=1 ;i<l ;i++){
        if( i>=p || i+nxt[i-a] >=p){
             if( i>=p)p=i;
             while( p<l && s[p] == s[p-i])p++;
             nxt[i] = p-i;
             a=i;
        }else nxt[i] = nxt[i-a];
     }
}

int main( ){
    int t;
    scanf("%d" ,&t);
    while( t--){
        scanf("%s" ,s);
        mem( nxt ,0 );
    int l =strlen(s);
    getnext( l );
    ll sum = 0;
    rep( i ,1 ,l-1 ){
    if(nxt[i])sum+=min( l-1-i,nxt[i]);
    }
    printf("%lld\n" ,sum+l-1);
    }
    return 0;
}

 

 

6630 permutation 2

题意:给定n,x,y,p[1~n]是1到n的一个全排列,求满足下列条件的全排列的个数:

分析:

1.转化成相似问题:有1~n的格子,每次可往左或右跳1或2步,起点为x,终点为y,求x到y走遍所有格子有几种走法

2.手动模拟一下就会注意到 x --> 1 -->x+1 和 y-1 --> n --> y 这两段路径走法是固定的,所以转化为x+1到y-1走遍其之间的格子有几种走法

在手动模拟一下,要想不遗漏又不至于无路可走,一次只能前进1或前进3,然后dp即可

3.dls直播中说他是直接打表找规律的,也许打表找规律比思维更快些

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>

#define fi first
#define se second
#define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
#define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
#define mem( a ,x ) memset( a ,x ,sizeof(a))
using namespace std;

typedef long long ll;
typedef pair<int ,int> pii;
typedef pair<ll ,ll> pll;
typedef pair<string ,int> psi;

const int N = 100005;
const ll mod = 998244353;
int n ,x ,y ,t;
ll dp[100050];

int main( ){
    dp[1] = 1; dp[2] = 1; dp[3] = 1;
    rep( i ,4 ,N)dp[i] = (dp[i-1] + dp[i-3])%mod;
    scanf("%d" ,&t);
    while( t-- ){
        scanf("%d%d%d" ,&n ,&x ,&y);
        if( x>y )swap(x ,y);
        int k = y-x -1;
        if( x==1 )k++;
        if( y==n )k++;
        printf("%lld\n" ,dp[k]);
    }
    return 0;
}

 

6628 permutation 1

题意:给定n,k,求1~n的全排列p[1~n]中,p[2]-p[1] ,p[3]-p[2] ,p[4]-p[3] ......  的字典序第k小的全排列 ,其中n<=20 ,k<=10000

分析:

1.打表发现 ,当第一位是n时,后面1~n-1的全排列字典序和p[2]-p[1] ,p[3]-p[2] ,p[4]-p[3]的字典序是一样的,直接next_permutation枚举就行

当第一位变化时,规律就不明显了

2. 因为8! > 10000 ,所以n > 8 时 ,只管后面8位就行,而不管n是多少,对于相同k而言这8位的相对顺序是固定的,结合1直接预处理下1~8全排列的字典序就行

3.当n<8时,直接根据题意的判断条件sort一下1~n的所有全排列输出第k个即可

比赛时不会重载数组的比较运算符没写出来,要注意一下这个重载方式

暴力代码:

 

#include <bits/stdc++.h>
using namespace std;

struct P{
    int a[10] ,b[10];
    int l;
    
    bool operator < ( const P & s )const{
        for( int i = 2 ;i<=l ;i++ ){
            if( b[i] < s.b[i] )return 1;
            if( b[i] > s.b[i] )return 0;
        }
        return 0;
    }
}p[50000];

int T ,k ,n;
int p_pre[50000][10];
int main( ){
    int a[25];
    for( int i=1 ;i<=8 ;i++ ){
         a[i] = i;
    }
    
    int cnt = 0;
    do{
        cnt++;
        for( int i=1 ;i<=8 ;i++)
            p_pre[cnt][i] = a[i];
    }while( next_permutation( a+1 ,a+9 ) );
    
    scanf("%d" ,&T);
    while( T-- ){
        scanf( "%d%d" ,&n ,&k );
        if( n <=8 ){
            for( int i=1 ;i<=n ;i++ )a[i] = i;
            cnt = 0;
            do{
                cnt++;
                for( int i=1 ;i<=n ;i++){
                    p[cnt].l = n;
                    p[cnt].a[i] = a[i];
                    p[cnt].b[i] = a[i]-a[i-1];
                }
            }while( next_permutation( a+1 ,a+1+n ));
            
            sort( p+1 ,p+cnt+1 );
            
            for( int i=1 ;i<=n ;i++ ){
                if( i>1 )printf(" ");
                printf("%d" ,p[k].a[i] );
            }
        }
        else{
            a[1] = n;
            
            for( int i=2 ; i<=n-8 ;i++ ){
                a[i] = i-1;
            }
            
            for( int i=n-8+1 ;i<=n ;i++ ){
                a[i] = p_pre[k][i-n+8]+n-8-1;
                cout<<i<<" "<<a[i]<<endl;
            }
            for( int i=1 ;i<=n ;i++ ){
                if( i>1 )printf(" ");
                printf("%d" ,a[i]);
            }
        }
        printf("\n");    
    }
    
    return 0;
}

 

但是这不是标准题解,由于next_permutation一次平均复杂度是O(n),所以n<8时时间复杂度是O(n!n*nlogn) ,这复杂度十分爆炸,只要k的范围扩大就100%会tle

标准题解是dfs构造第k小排列,复杂度是 O(kn^2) ,不是很懂 ,先码为敬

#include<bits/stdc++.h>

#define LL long long
#define fi first
#define se second
#define mp make_pair
#define pb push_back

using namespace std;

LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
LL lcm(LL a,LL b){return a/gcd(a,b)*b;}
LL powmod(LL a,LL b,LL MOD){LL ans=1;while(b){if(b%2)ans=ans*a%MOD;a=a*a%MOD;b/=2;}return ans;}
const int N = 503;
int t,n,k;
int vis[N],p[N];
bool dfs(int now,int pre,int l,int r){
    if(now==n){
        if(k==1){
            for(int i=0;i<n;i++){
                cout<<p[i]-l+1;
                if(i<n-1)cout<<' ';
                else cout<<'\n';
            }
            return 1;
        }
        k--;
        return 0;
    }
    for(int i=1-n;i<=n-1;i++){//枚举差异序列字典序最小
        if(!vis[i+pre]){
            vis[i+pre]=1;
            if(max(i+pre,r)-min(l,i+pre)<=n-1){
                p[now]=i+pre;
                if(dfs(now+1,i+pre,min(i+pre,l),max(i+pre,r))){
                    vis[i+pre]=0;
                    return 1;
                }
            }
            vis[i+pre]=0;
        }
    }
    return 0;
}
int main(){
    ios::sync_with_stdio(false);
    for(cin>>t;t;t--){
        cin>>n>>k;int sta=0;
        vis[n]=1;
        p[0]=n;
        dfs(1,n,n,n);//相对大小
        vis[n]=0;
    }    
    return 0;
}

 

6627 equation

题意:给定数组a,b,解方程 

 

分析: 出现绝对值符号先去绝对值符号,发现分界点是x与 -bi/ai 的大小关系 ,对1~n个区间分类讨论即可 ,注意无数个解的情况

代码:

#include<bits/stdc++.h>

#define fi first
#define se second
#define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
#define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
#define mem( a ,x ) memset( a ,x ,sizeof(a))
using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int ,int> pii;
typedef pair<ll ,ll> pll;
typedef pair<string ,int> psi;

struct node{
    ll a ,b;
    bool operator < (const node & s )const{
        return b*s.a > a*s.b;
    }
}p[100050];
ll sa[100050] ,sb[100050];
ll ansa[100050] ,ansb[100050];
ll t ,n ,c;

int main( ){
    scanf("%lld" ,&t );
    while( t-- ){
        scanf("%lld%lld" ,&n ,&c );
        mem( sa ,0 ); mem( sb ,0 );
        rep( i ,1 ,n ){
            scanf("%lld%lld" ,&p[i].a ,&p[i].b);
            sa[i] = sa[i-1] + p[i].a;
            sb[i] = sb[i-1] + p[i].b;
        }

        sort( p+1 , p+1+n );
        p[n+1].a = p[n+1].b = 0;
        int cnt = 0;
        ll xa ,xb ,g;
        reb( i ,n ,0 ){
            xa = c-sb[n];
            xb = sa[n];
            //cout<<" now "<<p[i].a<<" "<<p[i].b<<endl;
            if( xb <0 )xa*=-1 ,xb*=-1;
            //cout<<sa[n]<<" "<<sb[n]<<endl;
           // cout<<"xa xb "<<xa<<" "<<xb<<endl;
            if( xa*p[i].a + xb*p[i].b >=0 && xa*p[i+1].a + xb*p[i+1].b <= 0 ){
                if( xb !=0 ){
                        g = __gcd(abs(xa) ,abs(xb));
                        //cout<<g<<endl;
                        ansa[++cnt] = xa/g;
                        ansb[cnt] = xb/g;
                        if( ansa[cnt] == ansa[cnt-1] && ansb[cnt] == ansb[cnt-1] )cnt--;
                }
                else if( xa == 0 ){ cnt = -1; break; }

            }
            sa[n] -= 2*p[i].a;
            sb[n] -= 2*p[i].b;
        }

        printf("%d" ,cnt);
        reb( i ,cnt ,1 )printf(" %lld/%lld" ,ansa[i] ,ansb[i] );
        printf("\n");
    }

    return 0;
}

 

|pipi+1|2

 6625 three arrays

题意: 给定数组a,b,构造出ci = ai^bi 使得c数组字典序最小

分析:

代码:

#include<bits/stdc++.h>

#define fi first
#define se second
#define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
#define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
#define mem( a ,x ) memset( a ,x ,sizeof(a))
using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int ,int> pii;
typedef pair<ll ,ll> pll;
typedef pair<string ,int> psi;

const int maxn = 100005;
int cnt[2];

int trie[2][maxn*31][2] ;
int sum[2][maxn*31][2] ;
int t ,n ,ans[maxn];

inline void Insert(int id ,int x){
       int root = 0  ,tmp;
    reb( i ,30 ,0 ){
        tmp = (x>>i) & 1;
        if( !trie[id][root][tmp] )trie[id][root][tmp] = ++cnt[id];
        sum[id][root][tmp] ++;
        //cout<<" id x "<<id<<" "<<x<<endl;
        //cout<<"root sum "<<root<<" "<<sum[id][root][tmp]<<endl;
        root = trie[id][root][tmp];
    }
}


inline void Delete(int id ,int x ){
    int root = 0 ,tmp ,tot;
    reb( i ,30 ,0 ){
        tmp = (x>>i) & 1;
        sum[id][root][tmp] --;
        //cout<<" root "<<root<<endl;
        //cout<<" son "<<trie[id][root][tmp];
        //cout<<" sum "<<sum[id][root][tmp]<<endl;
        if( !sum[id][root][tmp] ){
           tot = root;
           root = trie[id][root][tmp];
           trie[id][tot][tmp] = 0;
        }
        else root = trie[id][root][tmp];
        if( !root )break;
    }
}

inline void init( ){
    //mem( trie , 0 );
    cnt[0] = cnt[1] = 0;
}

inline int Find( int x ,int y ){
    x = y = 0;
    int root_a =0 , root_b = 0;
    reb( i ,30 ,0 ){
        if( sum[0][root_a][0] && sum[1][root_b][0] ){
            root_a = trie[0][root_a][0];
            root_b = trie[1][root_b][0];
            continue;
        }
        
        if( sum[0][root_a][1] && sum[1][root_b][1] ){
            root_a = trie[0][root_a][1];
            root_b = trie[1][root_b][1];
            x |= (1<<i); y |= (1<<i);
            continue;   
        }
        
        if( sum[0][root_a][0] && sum[1][root_b][1] ){
            root_a = trie[0][root_a][0];
            root_b = trie[1][root_b][1];
            y |= (1<<i);
            continue;   
        }
        
        if( sum[0][root_a][1] && sum[1][root_b][0] ){
            root_a = trie[0][root_a][1];
            root_b = trie[1][root_b][0];
            x |= (1<<i);
            continue;   
        }
    }
    //cout<<x<<" "<<y<<endl;
    Delete( 0  ,x );
    Delete( 1  ,y );
    return x^y;
}

int main( ){
    //freopen( "out.txt" ,"w" ,stdout );
    scanf("%d" ,&t );
    while( t-- ){
        scanf("%d" ,&n );
        int now;
        init( );
        
        rep( i ,1 ,n ){
            scanf("%d" ,&now );
            Insert(0 ,now );
        }
        
        rep( i ,1 ,n ){
            scanf("%d" ,&now );
            Insert(1 ,now );
        }
        
        rep( i ,1 ,n ){
            ans[i] = Find( 0 ,0 );
        }
        sort( ans+1 ,ans+1+n );
        rep( i ,1 ,n ){
            if(i!=1)printf(" ");
            printf("%d" ,ans[i]);
        }
        printf("\n");
    }
    return 0;
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!