zoukankan      html  css  js  c++  java
  • 【2019.10.6】备战NOIP2018模拟赛7

    总结

    我绝对是今天没睡醒/没带脑子来考的试。

    T1:这种题主要错在不经常做。大方向是对的,往数学方面想,因为显然其他算法实现不了。推出来了模数的部分,但没想到最多加到2,应该可以考虑到的,因为加数为0~12显然实现不了。

    (“一道非常签到的暴搜题”。签到题我都没做出来..活该做题少吗。)

    T2:思路完全正确,被我给打炸了

    (没睡醒的证明...感觉做过,想了想唯一的解释就是我没睡醒。)

    T3:“数论和数据结构的好题”。没时间看过了过了。

    那么怎么解决没带脑子来考试的问题呢?-前天晚上休息好,平时打题认真打。要是实在没带脑子,先休息会儿/洗把脸/走一走清醒下,效率绝对比强撑着高(属实下策,没得办法)。

    考试第三题没时间看,暴力分都拿不到。-3h30min的考试的时间分配要想好。一、二题最多各花40min~1h,那么就要建立在平时大量的练习基础上。第三题则是靠先拿暴力分,建立在平时做难题的基础上。

    总的来说这套题还是蛮好的,感觉很复古(?),考的内容大多基础且很久没在模拟考中碰到了,今后刷题注意一些基础题+提高题的混合练习。

     

    后缀数组(zzx,1s,32MB)

    【题目描述】

    xyz1048576正在玩一个关于矩阵的游戏。

    一个n*m的矩阵,矩阵中每个数都是[1,12]内的整数。你可以执行下列两个操作任意多次

    (1)指定一行,将该行所有数字+1。

    (2)指定一列,将该列所有数字+1。

    (3)如果执行完上述操作之后,矩阵中某个数变成了3,6,9,12其中的某一个,我们认为这个数是稳的。

    给定初始矩阵,求出任意执行操作之后稳数的最多个数。

    【输入格式

    第一行包含两个正整数n,m。

    接下来n行,每行m个数,描述这个矩阵。

    【输出格式

    一个整数,表示答案。

    【输入样例1

    3 3

    1 2 3

    3 2 4

    1 2 1

    【输出样例1

    7

    【输入样例2】

    5 5

    2 4 6 8 10

    1 2 3 4 5

    3 4 5 6 7

    7 8 9 10 11

    5 10 12 3 7

    【输出样例2】

     20

    【数据规模及约定

    对于10%的数据,n,m≤2。

    对于20%的数据,n,m≤5。

    对于100%的数据,n,m≤10。


     题解

     有两个关键要想通。

    1.每一行(或者列,为方便,下文全部考虑成行,不影响结果)加上的数只可能是0~2。因为如加数果大于等于3的话,结果减去3仍为非负整数,3的倍数的个数不变,小于12的数的个数只会更多或不变。

    2.怎么使3,6,9,12(3的倍数)可能地多呢?显然两个数x,y可以同时变成3,6,9,12之一的条件是x≡y%3。所以暴力枚举行的加数,再统计行最多出现次数即可。所以出现的最多次数为max(%3余0的数的个数,%3余1的数的个数,%3余2的数的个数)


     代码

    #include<bits/stdc++.h>
    #define For(i,l,r) for(int i=l;i<=r;i++)
    using namespace std;
    const int M=14;
    int n,m,a[M][M],ans,delta[M],cnt[M],ntc[M];
    inline int read(){
        int f=1,sum=0;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
        return f*sum;
    }
    inline void dfs(int dep){
        if(dep==n+1){
            int sum=0;
            For(i,1,m){
                For(j,1,12) cnt[j]=0;
                For(j,0,2) ntc[j]=0;
                For(j,1,n)
                  if(a[j][i]+delta[j]<=12)
                    cnt[a[j][i]+delta[j]]++;
                For(j,1,12) ntc[j%3]+=cnt[j];
                int num=0;
                For(j,0,2) num=max(num,ntc[j]);
                sum+=num;
            }
            ans=max(ans,sum);
            return;
        }
        delta[dep]=0;dfs(dep+1);
        delta[dep]=1;dfs(dep+1);
        delta[dep]=2;dfs(dep+1);
    }
    int main(){
        freopen("zzx.in","r",stdin);
        freopen("zzx.out","w",stdout);
        n=read(),m=read();
        For(i,1,n)
          For(j,1,m)
            a[i][j]=read();
        dfs(1);
        printf("%d",ans);
        return 0;
    } 
    View Code

    后缀树(ysy,1s,32MB

    【题目描述】

    infinite32768正在研究一个关于排列的问题。

    给定一个长度为偶数的排列p,你每次可以选取p排列中相邻的两个元素,假如分别是x和y,那么把x和y加入一个序列q的末尾,并将x和y从排列p中删除。重复上述过程,直到p中没有元素,显然最终q序列也是一个排列。例如p=(1,3,2,4),第一次可以选取3,2这两个元素加入q,并且从p中删除,此时p=(1,4),第二次可以选取1,4这两个元素加入q,此时p为空,q=(3,2,1,4)。观察上述过程,最终q序列可能会有很多方案,请你输出所有可能方案中,q的字典序最大的。

    字典序的比较方式如下,假设有两个长度同为n的序列a,b。我们找到最大的t,使得∀i≤t,a[i]=b[i]。之后比较a[t+1]与b[t+1],如果a[t+1]<b[t+1],我们就认为a的字典序比b小,反之亦然。

    【输入格式】

    第一行包含一个正整数 n。第二行 n 个数,表示排列 p。

    【输出格式】

    一行 n 个数,表示字典序最大的序列 q。

    【输入样例1

    4

    3 1 4 2

    【输出样例1

    4 2 3 1

    【输入样例2】

    6

    6 5 4 1 3 2

    【输出样例2】

    6 5 4 1 3 2

    【数据规模及约定】

    对于20%的数据,n≤10。

    对于60%的数据,n≤1,000。

    对于100%的数据,n≤100,000。


    题解(懒得打因为真的很简单):

    字典序显然可以贪心取。

    贪心法则:连续n/2次,每次考虑p中剩下的数的最大值x和次大值y,如果x不是p的最后一个元素,则把x和x在p中的下一个元素依次加入q的末尾。

    如果x是p的最后一个元素,则y一定不是p的最后一个元素。这时候把yyp中的下一个元素依次加入q的末尾。

    可以使用优先队列动态维护p中剩下数的最大值。

    Q:如何求得xp中的下一个元素呢?

    A:从左到右把p建成链表,删除元素时改变指针即可。


     代码

    #include<bits/stdc++.h>
    #define For(i,l,r) for(int i=l;i<=r;i++)
    #define ll long long 
    #define ri register int
    using namespace std;
    const int M=1e5+5;
    int n,m,a[M],pre[M],nxt[M],ans[M];
    priority_queue<int> pq,del;
    inline void maintain(){
        while(!pq.empty()&&!del.empty()&&(pq.top()==del.top())) 
          pq.pop(),del.pop();
    }
    inline int read(){
        int f=1,sum=0;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
        return f*sum;
    }
    int main(){
        freopen("ysy.in","r",stdin);
        freopen("ysy.out","w",stdout);
        n=read();
        For(i,1,n) a[i]=read();
        For(i,1,n-1) pre[nxt[a[i]]=a[i+1]]=a[i];//新写法get√
        For(i,1,n) pq.push(i);
        For(i,1,n>>1){
            maintain();
            int p=pq.top();pq.pop();
            maintain();
            int q=pq.top();pq.pop();
            if(nxt[p]){
                pq.push(q);q=nxt[p];
                if(pre[p]) nxt[pre[p]]=nxt[q];
                if(nxt[q]) pre[nxt[q]]=pre[p];
                del.push(q);
                ans[++m]=p;ans[++m]=q; 
            }
            else{
                pq.push(p);p=q;q=nxt[p];
                if(pre[p]) nxt[pre[p]]=nxt[q];
                if(nxt[q]) pre[nxt[q]]=pre[p];
                del.push(q);
                ans[++m]=p;ans[++m]=q; 
            }
        } 
        For(i,1,n) printf("%d ",ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    allocator类
    智能指针shared_ptr
    字面值常量类
    转换构造函数
    委托构造函数
    访问说明符&封装
    const成员函数
    函数指针
    constexper和常量表达式
    函数返回数组指针
  • 原文地址:https://www.cnblogs.com/jian-song/p/11627214.html
Copyright © 2011-2022 走看看