【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
#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]

【输入格式】

第一行包含一个正整数 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
#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

你可能感兴趣的:(【2019.10.6】备战NOIP2018模拟赛7)