[HIT 2018 暑期集训 precontest1]

题目来源:
2017 JUST Programming Contest 4.0
CF:http://codeforces.com/gym/101532
vjudge: https://vjudge.net/contest/238023#overview

A.
给一个序列{an},beauty(l,r)=a[l] & a[l+1] & a[l+2] & … & a[r] 。求
在这里插入图片描述
数列长度 &lt; = 1 0 5 &lt;=10^5 <=105,数字大小 &lt; = 1 0 6 &lt;=10^6 <=106

题解:
刚开始还懵逼了一会儿,果然是变菜了。
将所有数字转二进制,对所有数的二进制按位考虑,考虑加入集合一个数a[i],对于已经处于第T横向连续的长度为S的一串数字,若当前加入的数字a[i]的第T个二进制位为1,则这个数字的第T位在答案中的贡献就是(S+1)<<(T);若第T个二进制位为0,则这个数字在这个位上对答案贡献为0。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define ll long long
#define LiangJiaJun main
using namespace std;
int n,x;
ll ans,c[34];
int w33ha(){
    ans=0;
    memset(c,0,sizeof(c));
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        for(int j=0;j<32;j++){
            if((x>>j)&1){
                ++c[j];
                ans+=(1LL*c[j])<<j;
            }
            else{
                c[j]=0;
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

B.
给一个数列,有一些元素被挖掉了(被挖掉的为-1),原数列满足 a [ i ] = ( a [ i − 1 ] + 1 ) a[i]=(a[i-1]+1)%m a[i]=(a[i1]+1)
给定 n , m n,m n,m
m &lt; = 1 0 9 m&lt;=10^9 m<=109,数列长度 n &lt; = 1000 n&lt;=1000 n<=1000。给定被挖掉一些元素的数列,让你还原原数列,保证存在唯一解。

题解:
找到一个没被挖掉的数字然后左右直接推就好了。模拟题。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LiangJiaJun main
#define ll long long
using namespace std;

int n,m;
ll a[1004];
int w33ha(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    int st=0;
    for(int i=1;i<=n;i++){
        if(a[i]!=-1)st=i;
    }
    for(int i=st-1;i>=1;i--){
        a[i]=(a[i+1]-1+m)%m;
    }
    for(int i=st+1;i<=n;i++){
        a[i]=(a[i-1]+1)%m;
    }
    for(int i=1;i<=n;i++)printf("%lld ",a[i]);
    puts("");
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

C.
给一个长度为n的序列,对于每一个 a [ i ] a[i] a[i],寻找一个 a [ j ] ( j ! = i ) a[j] (j!=i) a[j](j!=i),使得 ( a [ i ] + a [ j ] ) (a[i]+a[j]) (a[i]+a[j]) % M O D MOD MOD 最大。对于每一个 a [ i ] a[i] a[i]输出这个最大值。
数列长度 &lt; = 1 0 5 &lt;=10^5 <=105 数字大小 &lt; 1 0 9 + 7 &lt;10^9+7 <109+7
M O D = 1 0 9 + 7 MOD=10^9+7 MOD=109+7

题解:
前期脑残了一下,浪费了一些时间。
二分,先对所有数字从小到大排序,然后由于任意两个数字相加都不会超过2*MOD,那么我们需要找的a[j]就只有3种:
1.a[i]+a[j]< MOD的最大
2.最大的a[j]
3.小于等于a[i]中最小的a[j]
这个过程用二分查找就行了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LiangJiaJun main
#define MOD 1000000007
using namespace std;
int n;
struct Ds{
    int val,sit;
    int ans;
}a[100004];
inline bool dex(Ds A,Ds B){return A.val<B.val;}
inline bool cmp(Ds A,Ds B){return A.sit<B.sit;}

int w33ha(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].val);
        a[i].sit=i;
    }
    a[0].val=0;
    sort(a+1,a+n+1,dex);
    for(int i=1;i<=n;i++){
        int l,r,mid,ret=-1;
        if(i!=1)ret=(a[i].val+a[i-1].val)%MOD;
        l=1;r=i-1;
        while(l<=r){
            mid=(l+r)>>1;
            if(a[i].val+a[mid].val>=MOD){
                r=mid-1;
            }
            else{
                ret=max((a[i].val+a[mid].val)%MOD,ret);
                l=mid+1;
            }
        }
        if(i!=n)ret=max(ret,(a[i].val+a[n].val)%MOD);
        l=i+1;r=n;
        while(l<=r){
            mid=(l+r)>>1;
            if(a[i].val+a[mid].val>=MOD){
                r=mid-1;
            }
            else{
                ret=max((a[i].val+a[mid].val)%MOD,ret);
                l=mid+1;
            }
        }
        a[i].ans=ret;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)printf("%d ",a[i].ans);
    printf("\n");
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

D.
给一个长度为 n ( n &lt; = 1 0 4 ) n(n&lt;=10^4) n(n<=104)的只包含小写字母的字符串s,w=sssss…,w字符串由无数个s字符串首尾连接而成 ,然后有m询问,每组询问询问一个区间 l , r l,r l,r和一个字母,询问在字符串w上[l,r]的区间内有多少个这个字母。

题解:
对于询问有三种情况。

  1. l , r l,r l,r在同一个循环s内
  2. l , r l,r l,r在相邻的两个循环s内
  3. l , r l,r l,r在不相邻的两个循环s内
    我们只需要前缀和,然后对每个情况都讨论就好了
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define INF 1999122700
#define LiangJiaJun main
using namespace std;
int n,q;
char s[10004],ch[4];
int pre[10004][34];
ll calc(int x){
   ll g=x/n;
   if(x%n!=0)g++;
   return g;
}
int w33ha(){
    memset(pre,0,sizeof(pre));
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++){
        for(int j=0;j<26;j++){
            if(s[i]-'a'==j){
                pre[i][j]=pre[i-1][j]+1;
            }
            else{
                pre[i][j]=pre[i-1][j];
            }
        }
    }
    while(q--){
        int l,r;
        ll ans=0,pol,por;
        scanf("%d%d",&l,&r);
        scanf("%s",ch+1);
        pol=calc(l)+1;
        por=calc(r)-1;
        if(pol<=por){
            ans+=(por-pol+1LL)*pre[n][ch[1]-'a'];
        }
        else if(pol-2==por){
            printf("%lld\n",1LL*pre[r-por*n][ch[1]-'a']-pre[l-por*n-1][ch[1]-'a']);
            continue;
        }
        else{
            printf("%lld\n",1LL*pre[r-por*n][ch[1]-'a']+pre[n][ch[1]-'a']-pre[l-(por-1)*n-1][ch[1]-'a']);
            continue;
        }
        ans+=(pre[n][ch[1]-'a']-pre[l-(pol-2)*n-1][ch[1]-'a']);
        ans+=(pre[r-por*n][ch[1]-'a']);
        printf("%lld\n",ans);
    }
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

E.
n ( n &lt; = 14 ) n(n&lt;=14) n(n<=14)个骰子和一个数字X,每个骰子都有六个面,然后问有多少种投骰子的结果使得所有骰子朝上的那个面的数字的乘积对 1 0 9 + 7 10^9+7 109+7取模等于X

题解:
折半搜索,先求出前n/2个骰子投出的所有方案,用map存起来,然后再算后n-(n/2)个骰子投出的方案,去map里面找前n/2个骰子的与之匹配的结果,这个过程要用到求逆元。
难受,这题因为愚蠢,在最后没有rush出来。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#define ll long long
#define INF 1999122700
#define MOD 1000000007LL
#define LiangJiaJun main
using namespace std;
map<ll,ll>mo;
int n;
ll X,gf,ans=0;
int f[24][14];
ll fp(ll x,ll y){
   if(y==1)return x%MOD;
   if(y==0)return 1;
   ll temp=fp(x,y>>1);
   if(y&1){
      return (((temp*temp)%MOD)*x)%MOD;
   }
   else return (temp*temp)%MOD;
}
ll rev(ll x,ll m){
   return fp(x,m-2LL);
}
void dfs1(int x,int lim){
     if(x>lim){
        mo[gf]++;
        return ;
     }
     ll lp=gf;
     for(int i=1;i<=6;i++){
         gf=(gf*f[x][i])%MOD;
         dfs1(x+1,lim);
         gf=lp;
     }
}
void dfs2(int x,int lim){
     if(x>lim){
        ans+=mo[(X*rev(gf,MOD))%MOD];
        return ;
     }
     ll lp=gf;
     for(int i=1;i<=6;i++){
         gf=(gf*f[x][i])%MOD;
         dfs2(x+1,lim);
         gf=lp;
     }
}
int w33ha(){
    mo.clear();
    ans=0;
    scanf("%d%lld",&n,&X);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=6;j++){
            scanf("%d",&f[i][j]);
        }
    }
    gf=1;
    dfs1(1,n/2);
    gf=1;
    dfs2(n/2+1,n);
    printf("%lld\n",ans);
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

F.
留坑

G.
给一个长度为 n n n的序列,求满足下列条件的元素个数

  1. 1   &lt;   x   &lt;   n 1 &lt; x &lt; n 1<x<n
  2. a [ y ]   ≤   a [ x ] a[y] ≤ a[x] a[y]a[x], for each y ( 1   ≤   y   &lt;   x ) . (1 ≤ y &lt; x). (1y<x).
  3. a [ x ]   ≤   a [ z ] a[x] ≤ a[z] a[x]a[z], for each z ( x   &lt;   z   ≤   n ) . (x &lt; z ≤ n). (x<zn).

n &lt; = 1 0 6 , 1 &lt; = a [ i ] &lt; = 1 0 6 n&lt;=10^6 ,1&lt;=a[i]&lt;=10^6 n<=106,1<=a[i]<=106

题解:
简单题,用一个前缀最大值维护条件2,后缀最小值维护条件3, o ( n ) o(n) o(n)扫一遍就好了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define ll long long
#define INF 1999122700
#define LiangJiaJun main
using namespace std;
int n;
int a[1000004],b[1000004],c[1000004];
int w33ha(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    memset(b,0,sizeof(b));
    for(int i=0;i<=n+1;i++)c[i]=INF;
    for(int i=1;i<=n;i++){
        b[i]=max(b[i-1],a[i]);
    }
    for(int i=n;i>=1;i--){
        c[i]=min(c[i+1],a[i]);
    }
    int ans=0;
    for(int i=2;i<n;i++){
        if(a[i]>=b[i-1]&&a[i]<=c[i+1])++ans;
    }
    printf("%d\n",ans);
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

H.
给一个n*m的01矩阵,你可以交换任意两个位置的数字,询问至少需要交换多少次才能使得整个矩阵的第1行,第n行,第1列,第m列上全都是1。如果无法使那些位置都是1的话就输出-1
题解:
模拟题

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define INF 1999122700
#define LiangJiaJun main
using namespace std;
int n,m;
char mp[104][104];
int w33ha(){
    scanf("%d%d",&n,&m);
    int cnt1=0,cnt0=0;
    for(int i=1;i<=n;i++)scanf("%s",mp[i]+1);
    for(int i=2;i<n;i++){
        for(int j=2;j<m;j++){
            if(mp[i][j]=='1')++cnt1;
        }
    }
    for(int i=1;i<=n;i++){
        if(mp[i][1]=='0')++cnt0;
        if(mp[i][m]=='0')++cnt0;
    }
    for(int i=1;i<=m;i++){
        if(mp[1][i]=='0')++cnt0;
        if(mp[n][i]=='0')++cnt0;
    }
    if(mp[1][1]=='0')--cnt0;
    if(mp[1][m]=='0')--cnt0;
    if(mp[n][1]=='0')--cnt0;
    if(mp[n][m]=='0')--cnt0;
    if(cnt0>cnt1)puts("-1");
    else printf("%d\n",cnt0);
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

I.
n个一字排开的格子,第 i i i个格子的颜色为 c [ i ] c[i] c[i],刚开始玩家在第一个格子。接下来他可以选择:

  1. 往前跳一格。
  2. 跳到离自己最近的格子与自己格子颜色相同的格子。
    问从第一个格子到第n个格子的最少跳的步数是多少?
    n , c [ i ] &lt; = 2 ∗ 1 0 5 n,c[i]&lt;=2*10^5 n,c[i]<=2105
    题解:
    对于格子 i i i,连一条边 ( i − &gt; i + 1 ) (i-&gt;i+1) (i>i+1) 然后再连一条边从 i − &gt; i-&gt; i> i i i最近的颜色跟i相同的点,这个可以用vector解决。然后以1为起点跑一个bfs就可以知道最小的步数了。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define INF 1999122700
#define LiangJiaJun main
using namespace std;
queue<int>q;
int n,c[200004];
vector<int>a[200004];
struct edge{
    int to,nt,w;
}e[1000004];
int h[200004],ne;
int dis[200004];
int add(int u,int v){
    e[++ne].to=v;e[ne].nt=h[u];h[u]=ne;
}
void bfs(int st){
     for(int i=0;i<=200001;i++)dis[i]=-1;
     q.push(st);dis[st]=0;
     while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=h[x];i;i=e[i].nt){
            if( dis[e[i].to]==-1){
                dis[e[i].to]=dis[x]+1;
                q.push(e[i].to);
            }
        }
     }
     return ;
}
int w33ha(){
    for(int i=0;i<=200001;i++)a[i].clear();
    memset(h,0,sizeof(h));
    ne=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&c[i]);
        a[c[i]].push_back(i);
    }
    for(int i=1;i<n;i++)add(i,i+1);
    for(int i=1;i<=200000;i++){
        if(a[i].size()>1){
            for(int j=0;j<a[i].size()-1;j++){
                add(a[i][j],a[i][j+1]);
            }
        }
    }
    bfs(1);
    printf("%d\n",dis[n]);
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

J.
给定一个长度为 n n n的序列,定义这个序列的一个子集为序列删去任意个元素后剩余的所有元素,每个子集的价值为子集内所有元素的乘积。求所有子集价值的和对10^9+7取模的结果。
题解:
难受,差点死于DP
f[i]表示由前 i i i个元素组成的序列的所有子集价值之和。则得出递推式
f [ i ] = ( f [ i − 1 ] + ( f [ i − 1 ] ∗ a [ i ] f[i]=(f[i-1]+(f[i-1]*a[i] f[i]=(f[i1]+(f[i1]a[i]% M O D ) + a [ i ] ) MOD)+a[i]) MOD)+a[i])% M O D MOD MOD

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define INF 1999122700
#define MOD 1000000007LL
#define LiangJiaJun main
using namespace std;
int n,x;
ll f[100004];
int w33ha(){
    scanf("%d",&n);
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        f[i]=(f[i-1]+((f[i-1]*x)%MOD)+x)%MOD;
    }
    printf("%lld\n",f[n]);
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}

K.
给一个长度为n(n<=20)的字符串,询问将字符串重排后,是回文串的情况有多少种。
题解:
两眼一闭大力搜

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define INF 1999122700
#define LiangJiaJun main
using namespace std;
int n,cnt[34],ans=0;
char s[34];
int ep[14];
void dfs(int x){
     if(x>n){
        ++ans;
        return ;
     }
     for(int i=0;i<26;i++){
        if(cnt[i]){
            --cnt[i];
            dfs(x+1);
            ++cnt[i];
        }
     }
     return ;
}
int w33ha(){
    scanf("%d",&n);
    scanf("%s",s+1);
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;i++)cnt[s[i]-'a']++;
    int cd=0;
    ans=0;
    for(int i=0;i<26;i++)if(cnt[i]&1)++cd;
    if(cd>=2)return puts("0"),0;
    if(cd==1){
        for(int i=0;i<26;i++)if(cnt[i]&1)--cnt[i];
    }
    n>>=1;
    for(int i=0;i<26;i++)cnt[i]>>=1;
    dfs(1);
    printf("%d\n",ans);
    return 0;
}
int LiangJiaJun(){
    int T;scanf("%d",&T);
    while(T--)w33ha();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值