2018年5月12日周末练习1题解

题目:

2017 JUST Programming Contest 4.0

Jordan University of Science and Technology, September, 16, 2017

A:给你一个序列长度为n(n<=1e5),定义Beauty(l, r) = al & al + 1 & al + 2 & ... & ar,

其实自己在纸上推一下不难发现,我们只需要维护二进制中每一位连续为1的数量,然后每一次都加num[i]*2的i次方即可。

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 10010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
ll c[maxn],a[maxn];
int n,m,k;
ll num[maxn];
ll ans,tmp,cnt;
int main() {
    c[0]=1;
    for(int i=1;i<=28;i++)
    c[i]=c[i-1]*2;
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(num,0,sizeof(num));
        ans=0;
        for(int i=0;i<n;i++)
        {
            ll x;
            scanf("%lld",&x);
            for(ll j=0;j<=28;j++)
            {
                if(x&(1<<j)) num[j]++;
                else num[j]=0;
                ans+=num[j]*c[j];
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

B:给你一个序列,其中-1处为数字丢失处。而每个数满足a[i] = ( a[i-1]+ 1) % m,(1 < i ≤ n)(n<=1000,m<=1e9)

因此只需要找一个不为-1的数字分别往前、往后填上就行了。

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 100010
#define ll long long
#define inf 1e9+7
using namespace std;
int n,m,k;
ll a[maxn],c[maxn];
ll ans,tmp,cnt;
ll num[maxn];
int main() {
    int T,cas;
    c[0]=1;
    for(int i=1;i<=30;i++)
    c[i]=c[i-1]*2;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        ll ans;
        int t;
        memset(num,0,sizeof(num));
        for(int i=0;i<n;i++)
        {
            ll x;
            scanf("%lld",&a[i]);
            if(a[i]!=-1){ans=a[i];t=i;}
        }
        for(int i=t-1;i>=0;i--)
        {
            if(a[i]==-1) {a[i]=a[i+1]-1;if(a[i]<0) a[i]=(ll)(m-1);}
        }
        for(int i=t+1;i<n;i++)
        {
            if(a[i]==-1) a[i]=(a[i-1]+1)%(ll)m;
        }
    for(int i=0;i<n;i++)
    printf("%lld%c",a[i],i==n-1?'\n':' ');
    }
    return 0;
}

C:给你一个序列a,你要对每个a[i]找到一个a[j]使得(a[i]+a[j])%1000000007最大。(2<=n<=1e5)

不难想到,从小到大排一遍序以后,j=n-1,从最小的那个数开始(i=0),必定满足若a[i]+a[j]<mo,则a[j]就是a[i]找的那个数。

若>=mo,则j--;且满足使(a[i-1]+a[k])%mo最大的k一定小于等于j。

有几种特殊情况:

1、j=-1时,若i!=n-1,则一定(a[i]+a[n-1])%mo最大

2、j=-1时,若i==n-1,则一定则一定(a[i]+a[n-2])%mo最大

3、i==j时,j--;

题目不难,细节不少。

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 100010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
ll aa[maxn],c[maxn];
ll tmp,cnt;
ll ans[maxn];
int n;
struct node
{
    ll v;
    int id;
    bool operator<(node aa)const
    {
        return v<aa.v||v==aa.v&&id<aa.id;
    }
}a[maxn];
int main() {
    int T,cas;
    c[0]=1;
    for(int i=1;i<=30;i++)
    c[i]=c[i-1]*2;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int t;
        for(int i=0;i<n;i++)
        {
            scanf("%lld",&a[i].v);
            a[i].id=i;
        }
        sort(a,a+n);
        int j=n-1;
        for(int i=0;i<n;i++)
        {
            ll tmp=a[i].v;
            while(j>=0&&(tmp+a[j].v>=mo||(i==j))) j--;
            if(j<0)
            {
                if(i!=n-1) ans[a[i].id]=(tmp+a[n-1].v)%mo;
                else ans[a[i].id]=(tmp+a[n-2].v)%mo;
            }
            else  ans[a[i].id]=(tmp+a[j].v);
        }
    for(int i=0;i<n;i++)
    printf("%lld%c",ans[i],i==n-1?'\n':' ');
    }
    return 0;
}

D:给你一个只包含小写字母的无限长的串,循环节为s,长度为n(n<=1e4),求字母ch在下标为[l,r]长度的区间里出现了多少次。(l<=r<=1e9)

首先预处理一下前缀和,就是字母ch在0~i区间里出现了num[i][ch-'a']次。

然后对于区间[l,r](0<l<=r<n)中ch出现的次数即为num[r][ch-'a']-num[l-1][ch-'a'];

即对于l/len==r/len时,ans=num[r][ch-'a']-num[l-1][ch-'a'];

否则 ans=(r/len-l/len-1)*num[n-1][ch-'a']+num[r][ch-'a']+num[n-1][ch-'a']-num[l-1][ch-'a'];

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 10010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int aa[maxn],c[maxn];
int tmp,cnt;
int ans[maxn];
int num[maxn][27];
char s[maxn];
int n,m,q;
int find(int l,int r,int ch)
{
    if(l==0) return num[r][ch];
    else return num[r][ch]-num[l-1][ch];
}
int main() {
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&q);
        memset(num[0],0,sizeof(num[0]));
        int t;
        scanf("%s",s);
        for(int i=0;i<n;i++)
        {
            int id=s[i]-'a';
            if(i)
            for(int j=0;j<26;j++)
            num[i][j]=num[i-1][j];
            num[i][id]++;
        }
        for(int i=0;i<q;i++)
        {
            int x,y,id;
            char ch[2];
            scanf("%d %d %s",&x,&y,ch);
            id=ch[0]-'a';
            x--;y--;
            if((y/n)==(x/n))
            {
                x%=n;y%=n;
                int ans=find(x,y,id);
                printf("%d\n",ans);
            }
            else
            {
                int ans=(y/n)-(x/n)-1;
                ans*=num[n-1][id];
                x%=n;y%=n;
                ans+=find(x,n-1,id);
                ans+=find(0,y,id);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

E:给你n(n<=14)个骰子,每个骰子六个面都标有数字(1~100)你要从每个骰子选一个面,把面上的数字乘起来%mod(1e9+7)为m。求有多少种不同的选法。

上来就来了一发爆搜TLE,经提示后才知道神奇的折半法。。。

即先爆搜前一半骰子的乘积%mod后用map存起来其方法总数

再对后半段爆搜,对每个结果t,m*t的逆元%mod即为乘积等于m需要的数,直接map找其方法总数即可。

具体见代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 10010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
ll d[maxn],c[maxn];
ll tmp,cnt;
ll ans;
ll a[20][8];
int n,f1,f2,n1;
ll m,q;
map<ll,ll>mp;
ll power(ll a,ll n)   //a的n次方mod
{
    ll ans=1;
    a=a%mo;
    while (n)
    {
        if(n&1) ans=(ans*a)%mo;
        n>>=1;
        a=(a*a)%mo;
        }
    return ans;
}
void dfs(int i,ll tmp)
{
    if(i==n1)
    {
        if(mp.count(tmp)==0)
        {mp[tmp]=1;}
        else
        {
            mp[tmp]++;
        }
        return;
    }
    for(int j=0;j<6;j++)
    {
        dfs(i+1,(tmp*a[i][j])%mo);
    }
}
void dfs2(int i,ll tmp)
{
    if(i==n)
    {
        ans+=mp[(m*(power(tmp,mo-2)))%mo];
        return;
    }
    for(int j=0;j<6;j++)
    {
        dfs2(i+1,(tmp*a[i][j])%mo);
    }
}
int main() {
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d %lld",&n,&m);
        mp.clear();
        for(int i=0;i<n;i++)
        {
        for(int j=0;j<6;j++)
        {
            scanf("%lld",&a[i][j]);
        }
        sort(a[i],a[i]+6);
        }
        n1=(n+1)/2;
        dfs(0,1);
        dfs2(n1,1);
        printf("%lld\n",ans);
    }
    return 0;
}

F:题意是求x的总数,其中

1、1 < x < n (n<1e6)
2、ay ≤ ax, for each y (1 ≤ y < x).

3、ax ≤ az, for each z (x < z ≤ n).

只需要处理一下前缀最大值和后缀最小值,扫一遍就出来了。

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 1000010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int ma[maxn],mi[maxn];
int tmp,cnt;
int ans;
int a[maxn];
int n;
int m,q;
int main() {
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        ma[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            ma[i]=max(ma[i-1],a[i]);
        }
        mi[n+1]=inf;
        for(int i=n;i>=1;i--)
        {
            mi[i]=min(mi[i+1],a[i]);
        }
        int ans=0;
        for(int i=2;i<n;i++)
        if(a[i]>=ma[i-1]&&a[i]<=mi[i+1]) ans++;
        printf("%d\n",ans);
    }
    return 0;
}

G:给你一个n*m(n,m<50)的01棋盘,你要使其第一行、第一列、最后一行、最后一列的格子全为1,每次可以把任意两个格子交换数值,求最少需要交换多少次。如果不能使其第一行、第一列、最后一行、最后一列的格子全为1,则输出-1。

只需要记录边界上0的数量和非边界1的数量,比较一下即可。。。

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 1010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int tmp,cnt;
int ans;
int a[maxn];
int n;
int m,q;
char s[maxn][maxn];
int main() {
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
       // getchar();
        for(int i=0;i<n;i++) scanf("%s",s[i]);
        int ans=0,tmp=0;

        for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            int id=(s[i][j]&15);
            if(i==0||j==0||(i==n-1)||(j==m-1))
            {
                if(!id) ans++;
            }
            else if(id) tmp++;
        }
        //cout<<tmp<<ans<<endl;
        if(tmp<ans) puts("-1");
        else printf("%d\n",ans);
    }
    return 0;
}

H:给你n(n<2e5)个数a[1]~a[n],你刚开始在1的位置,你要跳到位置n。假设你现在在位置i

1、你可以跳到a[i]右边和a[i]值相等的最近的一个位置

2、你可以直接向右跳一个格子

求最少用多少部跳到位置n。

这不就是牛客网上某场大佬赛签到题的变种么。。。

记忆化bfs,记忆每个已经跳过的点。至于每个点可以往右跳几个预处理一下就行了。。。

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 200010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int tmp,cnt;
int ans;
int a[maxn],c[maxn];
int tiao[maxn];
int n;
int m,q;
struct node
{
    int x;
    int tmp;
}no;
void bfs()
{
    queue<node>q;
    no.x=1;
    no.tmp=0;
    q.push(no);
    c[1]=1;
    while(!q.empty())
    {
        node k=q.front(); q.pop();
        node kk=k;
        kk.tmp++;
        if(k.x==n)
        {
            printf("%d\n",k.tmp);
            return;
            }
        if(!c[k.x+1]){
        c[k.x+1]=1;
        kk.x++;
        q.push(kk);
        kk.x--;
        }
        if(tiao[kk.x]!=-1)
        {
            kk.x=tiao[k.x];
            if(!c[kk.x])
            {
            c[kk.x]=1;
            q.push(kk);
            }
        }
    }
}
int main() {
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(c,0,sizeof(c));
        memset(tiao,-1,sizeof(tiao));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(!c[a[i]]) c[a[i]]=i;
            else tiao[c[a[i]]]=i,c[a[i]]=i;
        }
        int ans=0;
        memset(c,0,sizeof(c));
        bfs();

    }
    return 0;
}

I:给你n(n<1e5)个数a[i]~a[n],你要求其中所有不重复子序列的乘积的和 mod 1e9+7。

又来了这道题,之前在牛客网上也见到过类似的,不过那是每个序列挑一个数的乘积总和。公式类似,直接求即可。

sum=sum+(sum+1)*a[i].(1<=i<=n)  公式很好推

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 200010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int tmp,cnt;
int ans;
ll a[maxn],c[maxn];
int n;
int m,q;
int main() {
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        ll ans=0;
        ll sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            ans=(ans+((sum+1)*a[i])%mo)%mo;
            sum=ans;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

J:给你一个串,长度为n(n<=20)求把其中的字母重新排列组合能形成的不重复的回文串的数量。

记录每个字母出现的次数,然后

1、n为偶数,有字母出现奇数次 答案为0

2、n为奇数,有多余一个字母出现奇数次 答案为0

3、否则   求n/2,即每个字母挑选a[i]个位置 即组合数C(n/2,a[i]) 注意求完后n/2要减去a[i]

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 210
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mod=1e9+7;
int tmp,cnt;
int ans;
int c[maxn];
int n;
int m,q;
char s[maxn];
int k,flag,x,f,y,p;
long long ni[maxn];
long long a[maxn];
long long zuhe(int x,int y){  //组合数
    return a[x]*ni[y]%mod*ni[x-y]%mod;
}
long long calc(long long x,long long y){
    long long z=1;
    while (y){
        if (y&1)(z*=x)%=mod;
        (x*=x)%=mod,y/=2;
    }
    return z;
}
int main() {
    a[0]=1;
    for (int i=1;i<maxn;i++)a[i]=a[i-1]*i%mod;
    ni[maxn-1]=calc(a[maxn-1],mod-2);
    for (int i=maxn-2;i>=0;i--)
    ni[i]=ni[i+1]*(i+1)%mod;
    int T,cas;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(c,0,sizeof(c));
        scanf("%s",s);
        for(int i=0;i<n;i++)
        {
            int id=s[i]-'a';
            c[id]++;
        }
        int tmp=0;
        for(int i=0;i<26;i++)
        {
           if(c[i]&1) tmp++;
        }
        if((!(n&1))&&(tmp)) puts("0");
        else if(tmp>1) puts("0");
        else {
            ll ans=1;
            n/=2;
            sort(c,c+26);
            for(int i=0;i<26;i++)
            {
                c[i]/=2;
                if(!n) break;
                if(c[i]){
                ans=ans*zuhe(n,c[i]);
                n-=c[i];
                }
            }
        printf("%lld\n",ans);
        }
    }
    return 0;
}

总体难度中等偏下,可惜由于今天考试,昨天的比赛没打,今天的估计也打不了了。。。


猜你喜欢

转载自blog.csdn.net/LSD20164388/article/details/80296762
今日推荐