20171009模拟赛总结

来源:不知道
分数:100+75+60
rank:10


T1 异或值

题意:

给定一个长度为n的序列,然后现在要在中间取出一个区间,求最大值^次大值的最大值
n<=2*10^6

分析:

一开始完全在分析异或有什么特点可以用在最大值次大值上的…一开始的想法是Trie树不过越往下想觉得第一题怎么可能那么麻烦。
然后就来看区间内最大值和次大值有什么特点了。
其实关于这类问题做得应该不算少了。大概有两种方法。

分治

solve(l,r)表示解决区间[l,r]内最大值最小值的问题。
首先扫一遍找到最大值,假设位置位于m,值为Mx。
那么向左向右各扫一遍,求出Mx为最大值的时候的次大值然后顺便更新一下答案。
然后solve(l,m-1); solve(m+1,r);

struct AAA{
    int res;
    void solve(int l,int r){
        int i,mid=l,Mx,mx1;
        for (i=l; i<=r; i++)if (a[i]>a[mid])mid=i;

        if (mid-1>l)solve(l,mid-1); 
        if (mid+1<r)solve(mid+1,r);
        Mx=a[mid];
        mx1=a[mid-1]; res=max(res,Mx^mx1);
        for (i=mid-1; i>=l; i--)if (a[i]>mx1){
            mx1=a[i];
            res=max(res,Mx^mx1);
        }
        mx1=a[mid+1]; res=max(res,Mx^mx1);
        for (i=mid+1; i<=r; i++)if (a[i]>mx1){
            mx1=a[i]; 
            res=max(res,Mx^mx1);
        }   
    }
    void sol(){
        solve(1,n);
        printf("%d\n",res);
    }
}pianfen;
单调栈

字面意思直接把每一对最大值和次大值整出来就好了

int main(){
    read(n);
    int i;
    for (i=1; i<=n; i++)read(a[i]);
    int top=0;
    for (i=1; i<=n; i++){
        for (; top&&a[i]>st[top]; top--)res=max(res,a[i]^st[top]);
        if (top)res=max(res,a[i]^st[top]);
        st[++top]=a[i];
    }
    top=0;
    for (i=n; i>=1; i--){
        for (; top&&a[i]>st[top]; top--)res=max(res,a[i]^st[top]);
        if (top)res=max(res,a[i]^st[top]);
        st[++top]=a[i];
    }
    printf("%d",res);
    return 0;
}

反思

没有想到单调栈其实是很可惜的,因为写分治的时候就觉得万一出数据卡一卡就会被打回O(n^2)了然后整场比赛都挺担心第一题。
好在没有特地出数据卡。
这里写图片描述


T2 原子的裂变

题意

用一个n位数来表示一个原子,每一位的数字大小∈[0,n],可以拥有前导0但是不能全是0。
现在一个长度为n的数,裂变后第一位表示这个数有几个1,第二位表示有几个2,依次。
例如:1104
裂变后就变成2001 (2个1,0个2,0个3,1个4)

有T组数据。对于每一组数据
读入一个n位数,求有多少个n位数可以裂变成它。

n<=9;T=100。

分析

p50

首先是暴力。比如对于now(一个n位数)来说。
cnt[i]表示第i位的数字是多大。
能变成他的必然满足有cnt[i]个i。然后就变成求一个排列。
写的是BFS表示方案,DFS求排列。

//dfs
void dfs(string s,int last,int sum){
    if (sum>last)return;
    if (last==0){if (!mp[s]){mp[s]=1;q.push(s);}return;}
    int i;
    if (last>sum){
        s1=s;
        s1+='0';
        dfs(s1,last-1,sum);
    }
    for (i=1; i<=len; i++)if (cnt[i]){
        s1=s;
        s1+='0'+i;
        cnt[i]--;
        dfs(s1,last-1,sum-1);
        cnt[i]++;
    }   
}

//bfs
void bfs(){
    for (; !q.empty(); res++){
        sum=0;
        now=q.front(); q.pop();
        for (i=0; i<len; i++)cnt[i+1]=now[i]-'0',sum+=cnt[i+1];
        if (sum>len)continue;
        dfs("",len,sum);
    }
}
p100

可以把所有数分成两类。
第一类是sum[cnt[i]]>n的,就当它为B类数,这类数字不能由其他的裂变而来。res=1
第二类是sum[cnt[i]]<=n的,就当它为A类数。res=所有可以一步变成它的res之和。

可以发现一个数的裂变过程中是不会产生环的。
除了1后面跟(n-1)个0的情况下会有一个自环,判掉就好了。

对于一个A类数,如果有一个B类数能转化成它,那么能转化成它的就全都是B类数。这里求个排列数就好了。
如果不是,那就强行求排列再往下递归就好了。

来自某同学的友情提供:即使是n=9的情况下,A类数的个数大约在40000左右。

#include<bits/stdc++.h>
using namespace std;
int n;
int jiecheng[10];
int solve(int x){
    int a[10],cnt[10];
    memset(a,0,sizeof(a));
    memset(cnt,0,sizeof(cnt));
    int k=0,l=0,tmp,i,j,res=0,t=x;
    for (i=n; i>=1; i--){tmp=t%10;cnt[i]=tmp;k+=tmp;l+=tmp*i;t/=10;}
    if (k>n)return 1;
    if (l>n){
        res=jiecheng[n];
        for (i=1; i<=n; i++)res/=jiecheng[cnt[i]];
        return res/jiecheng[n-k]+1;     
    }
    int id=n-k;
    for (i=1; i<=n; i++){
        for (j=1; j<=cnt[i]; j++)a[id++]=i;
    }   
    int s;s=0;
    for (i=0; i<n; i++)s=s*10+a[i];
    if (s!=x)res+=solve(s);     

    for (; next_permutation(a,a+n);){
        s=0;
        for (i=0; i<n; i++)s=s*10+a[i];
        if (s!=x)res+=solve(s);     
    }
    return res+1;
}

char s[15];

int main(){
    int t,l,i;
    scanf("%d",&t); jiecheng[0]=1;
    for (i=1; i<=9; i++)jiecheng[i]=jiecheng[i-1]*i;
    for (l=1; l<=t; l++){
        scanf("%s",s); n=strlen(s); int x=0;
        for (i=0; i<n; i++)x=x*10+(s[i]-'0');
        printf("Case #%d: %d\n",l,solve(x));        
    }
    return 0;   
}

反思

其实这道题还是挺满意的?水到了意料之外的75,一开始还以为只有50的,看来数据没有出长度全是最大值还都是100…0的情况。

T3 巧克力

题意

有n个旅游团。每个团都有a[i]个人。巧克力一盒有p块。吃完前不能开新的一盒。如果一个团内所有的人吃的都是新开的巧克力的话,那他们就会很开心。
求怎么分配能使开心的旅游团数量最多。
p<=4,n<=10000,a[i]<=1e9

分析

p好小Σ
p==2 所有偶数的先吃
然后奇数一人一个

p==3
所有三的倍数先吃
然后偶数一个奇数一个
最后奇数一个一个一个
cnt3+min(cnt1,cnt2)+(max(cnt1,cnt2)-min(cnt1,cnt2)+2)/3;

p==4
怎么觉得这题不算很难…?是我想错了莫?
总之4倍数的先吃。
好 吃完了。
然后剩下余数为1,2,3的
尽可能多把它们组成4的倍数。
首先22内销,然后13搞。
搞完再1111,3333,1133,1333,1113(就是1,3混起来4个)
然后最后112,332(两个1或3,加上一个2)
如果还有剩余就再res++

#include<cstdio>
#include<iostream>
#define M 10005
using namespace std;

void read(int &x){
    x=0; char c=getchar();
    for (; c<'0'; c=getchar());
    for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0');
}
int n,a[M];

struct AAA{
    void solve(){   
        int i;
        for (i=1; i<=n; i++)read(a[i]);
        printf("%d\n",n);
    }
}pianfen;

struct CCC{
    void solve(){
        int i,res=0,cnt=0;
        for (i=1; i<=n; i++){
            read(a[i]);
            if (a[i]&1)cnt++;   
            else res++;
        }
        printf("%d\n",res+(cnt+1)/2);
    }   
}p30;

struct ACC{
    void solve(){
        int i,res=0,cnt1=0,cnt2=0;
        for (i=1; i<=n; i++){
            read(a[i]);
            if (a[i]%3==0)res++;
            if (a[i]%3==1)cnt1++;
            if (a[i]%3==2)cnt2++;
        }   
        printf("%d\n",res+min(cnt1,cnt2)+(max(cnt1,cnt2)-min(cnt1,cnt2)+2)/3);
    }
}p60;

struct AAC{
    void solve(){
        int i,res=0,cnt1=0,cnt2=0,cnt3=0;
        for (i=1; i<=n; i++){
            read(a[i]);
            if ((a[i]&3)==0)res++;
            if ((a[i]&3)==1)cnt1++;
            if ((a[i]&3)==2)cnt2++;
            if ((a[i]&3)==3)cnt3++;
        }
        int num=min(cnt1,cnt3);
        res+=num; cnt1-=num; cnt3-=num;
        res+=cnt2/2; cnt2=cnt2&1;
        int s=(cnt1+cnt3);
        num=s/4;
        res+=num; s&=3;
        if (s>=2&&cnt2) res++,s-=2,cnt2--;
        if (s||cnt2) res++;
        printf("%d\n",res);     
    }   
}p100;

int main(){
//  freopen("chocolate.in","r",stdin);
//  freopen("chocolate.out","w",stdout);
    int t,l,p;
    read(t);
    for (l=1; l<=t; l++){
        printf("Case #%d: ",l); 
        read(n); read(p);
        if (p==1)pianfen.solve();
        if (p==2)p30.solve();
        if (p==3)p60.solve();
        if (p==4)p100.solve();
    }
    return 0;
}

反思

比赛的时候只敲到60,因为p==4的情况似乎弄错了一点结果一分都没有。
有点可惜不过还算很满意了。(因为题目好像不是很难)
这里写图片描述

总结

其实。很满意啦——没了。
非要说的话这几天也在写深搜,像第二题这种把状态分类来求的做法,还是挺有用的。能有效的减掉一层枝。
然后第三题的话没水到所有分有点可惜不过也在意料之内。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值