2017-10-09离线赛

据说是镇海中学出的题目

大体状况

255/300

T1 xor

题目大意

有一个数列,取一个区间,使区间最大值与次大值异或值最大。

分析
P40

暴力 O(n2)
维护最大值与次大值。

P100

这种题目肯定只有两种方法。
枚举最大值,查找次大值。
枚举次大值,查找最大值。
然后显然是后一种好写。
考虑其匹配的是哪个最大值。
用单调栈处理出前一个大于等于其的值。
设其位置为l,当前位置为r。
如果[l,r]范围扩大,这个答案不变或此数不能成为次大值。
如果[l,r]范围缩小,此数会变成最大值。
前后各扫一遍即可。

代码

然后数组少打一个0减少20分。
感觉是十分低级的错误啊。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define LL long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)

#define M 2000004

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}
int n,A[M];
struct P40{
    void Solve(){
        int Ans=0;
        REP(i,0,n-1){
            int x=max(A[i],A[i+1]),y=min(A[i],A[i+1]);
            chkmax(Ans,x^y);
            REP(j,i+2,n){
                if(A[j]>=x)y=x,x=A[j];
                else if(A[j]>y)y=A[j];
                chkmax(Ans,x^y);
            }
        }
        printf("%d\n",Ans);
    }
}P40;
struct P100{
    int Stk[M],Top;
    void Solve(){
        int Ans=0;
        REP(i,0,n){
            while(Top && Stk[Top]<A[i])Top--;
            if(Top)chkmax(Ans,Stk[Top]^A[i]);
            Stk[++Top]=A[i];
        }
        Top=0;
        DREP(i,n-1,-1){
            while(Top && Stk[Top]<A[i])Top--;
            if(Top)chkmax(Ans,Stk[Top]^A[i]);
            Stk[++Top]=A[i];
        }
        printf("%d\n",Ans);
    }
}P100;
int main(){
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);
    Rd(n);
    REP(i,0,n)Rd(A[i]);
    if(n<=5000)P40.Solve(); 
    else P100.Solve();
    return 0;
}

T2 atom

题目描述

求一棵看起来非常奇怪的树的子树节点个数。
然而边要自己造,点数达到 109

分析
P75

那么你就暴力建边暴力DP求一下就好了。
代码实现起来稍微麻烦一些。

P100

因为这个题目的各种性质,
所以剪掉叶节点后点数一下子就变成了 5105 以内。
然后叶节点可以排列组合算,
直接DFS或者一次DP处理均可。

代码

这里选择了对每种 n 建树并计算出所有答案。
然而没有DFS跑得快。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define LL long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)

int T,n,Case;
char C[14];
int Base[]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,404};
int Fac[]={1,1,2,6,24,120,720,5040,40320,362880,3628800};
struct P100{
    static const int M=50004;
    map<int,int>ID[14];
    bool Do[14];
    int Re[M],Sz[14][M];
    int Next[M],To[M],Head[M],Tim[M],id,tot,tar;
    void Add_Edge(int u,int v){
        if(Tim[u]!=tar)Tim[u]=tar,Head[u]=0;
        Next[++tot]=Head[u],To[Head[u]=tot]=v;
    }
    #define LREP(i,A) for(int i=Head[A];i;i=Next[i])
    void Clear(){
        tot=id=0;
        tar++;
    }
    void DFS(int A){
        Sz[n][A]=1;
        if(Tim[A]==tar)LREP(i,A)
            DFS(To[i]),Sz[n][A]+=Sz[n][To[i]];
        if(Sz[n][A]==1){
            int p=Re[A],k=Fac[n],zero=n;
            if(p==1)return;
            REP(i,0,n){
                k/=Fac[p%10];
                zero-=p%10;
                p/=10;
            }
            k/=Fac[zero];
            Sz[n][A]+=k;
        }
    }
    int A[14];
    void Build(int pos,int x,int S,bool k){
        if(pos>n){
            if(!S)return;
            if(k){
                int y=0;
                REP(i,1,n+1)
                    y+=Base[A[i]];
                if(y!=x)Add_Edge(ID[n][y],ID[n][x]);
            }
            else ID[n][x]=++id,Re[id]=x;
            return;
        }
        REP(i,0,(n-S)+1)
            A[pos]=i,Build(pos+1,x+Base[pos]*i,S+i,k);
    }
    void Init(){
        if(Do[n])return;
        Clear();
        Build(1,0,0,0);
        Build(1,0,0,1);

        DFS(ID[n][1]);
        Sz[n][ID[n][0]=0]=0;
        Do[n]=1;
    }
    void Solve(){
        Init();
        int x=0,j=0;
        REP(i,0,n){
            if(C[i]-'0'>n){printf("Case #%d: %d\n",++Case,0);return;} 
            j+=C[i]-'0';
            x+=Base[i+1]*(C[i]-'0');
        }
        printf("Case #%d: %d\n",++Case,j>n?1:Sz[n][ID[n][x]]);
    }
}P100;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%s",C);
        n=strlen(C);
        P100.Solve();
    }
    return 0;
}

T3 chocolate

分析

假的T3。

P30

有一个p2
然后就可以简单贪心。
p==1 时,答案肯定是 n
p==2时,肯定先喂%p=0再喂%p=1,然后这个随便算。

P60

然后是 p3
感觉还是能贪心。
先喂%p=0,然后%p=1或%p=2都是需要三个相同才能得到一个收益。
然后它们配对的话两个就可以得到一个收益。
所以算一下就好了。

P100

然后是 p4
先想了想枚举一个东西再贪心。
然后发现没有必要。
其实按着上面的思路接着分类讨论一下贪心即可。
具体细节看代码吧。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define LL long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}
#define M 10004 
int T,n,p,A[M],Case;
struct P30{
    int Cnt[4];
    void Solve(){
        memset(Cnt,0,sizeof(Cnt));
        REP(i,0,n)Cnt[A[i]&1]++;
        int Ans=Cnt[0]+(Cnt[1]>>1)+(Cnt[1]&1);
        printf("Case #%d: %d\n",++Case,Ans);
    }
}P30;
struct P60{
    int Cnt[4];
    void Solve(){
        memset(Cnt,0,sizeof(Cnt));
        REP(i,0,n)Cnt[A[i]%3]++;
        int Ans=min(Cnt[1],Cnt[2]);
        Cnt[1]-=Ans;
        Cnt[2]-=Ans;
        Ans+=(Cnt[1]+3-1)/3;
        Ans+=(Cnt[2]+3-1)/3;
        Ans+=Cnt[0];
        printf("Case #%d: %d\n",++Case,Ans);
    }
}P60;
struct P100{
    int Cnt[4];
    void Solve(){
        memset(Cnt,0,sizeof(Cnt));
        REP(i,0,n)Cnt[A[i]%4]++;
        int Ans=min(Cnt[1],Cnt[3]);
        Cnt[1]-=Ans;
        Cnt[3]-=Ans;
        int p=Cnt[1]==0?3:1; 
        Ans+=(Cnt[2]>>1);
        Cnt[2]=Cnt[2]&1;
        if(Cnt[2]){
            if(Cnt[p]>=2){
                Ans++,Cnt[p]-=2;
                Ans+=(Cnt[p]+4-1)/4;
            }
            else Ans++;
        }
        else Ans+=(Cnt[p]+4-1)/4;
        Ans+=Cnt[0];
        printf("Case #%d: %d\n",++Case,Ans);
    }
}P100;
int main(){
    freopen("chocolate.in","r",stdin);
    freopen("chocolate.out","w",stdout);
    Rd(T);
    while(T--){
        Rd(n),Rd(p);
        REP(i,0,n)Rd(A[i]);
        if(p==1)printf("Case #%d: %d\n",++Case,n);
        else if(p==2)P30.Solve();
        else if(p==3)P60.Solve();
        else P100.Solve();
    }
    return 0;
}

总结

十分正常的题目与分数。
时间分配略少了一些,否则应该能写出T2。
T2的P75实现花的时间较多,然而暴力DFS不优化也可以过。
T3是假T3,T1失误。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值