【浮*光】 #noip模拟10.24#

我真的.真的..真的...炒鸡炒鸡高兴的说!

因为,在noip前16天,我快要退役的时候,居然 ...

AK辣 ~\(≧▽≦)/~ 啦啦啦

好吧我承认我是 295分 ......

而且这么高是因为第二题抄了 斜率优化 的模板,

第三题和 LRZ大神 讨论了并且抄了他的dfs ......

可能是这套题比较符合我的口味吧?

而且很快的切掉了第一题...特别顺利的说!

反正无论如何,就是很高兴啊、很高兴啊 ovo

感谢 wuli 大男神 && ZJ && Rin && 各大男神 qwq


 目录

【T1】听写(dictation)--- 英语题 + 找规律

【T2】遥远的金字塔(pyramid)--- 数学题 + 斜率优化 DP

【T3】心灵治愈(heal) --- 语文题 + 数论 + DFS


【T1】听写(dictation)

  • 老师给大家一种全新的语言,要你来鉴别它们是不是合法的。

1.一个句子由主语、谓语和宾语按先后顺序排列而成,

   缺少其中任何一个成分都不是这种语言;

2.语言中存在两种从句:主语从句和定语从句;

3.对于主语从句,从句里可以有新的从句,

   从句只可替代应有的部分,所以并不会出现形式主语等;

4.对于有定语从句的句子,定语从句只能修饰宾语(放在宾语后) ,

   而且其修饰的宾语应该省略,定语从句里也可以有新的从句。

  • 为了简化问题,用 z 代表主语,w 代表谓语,b 代表宾语。

【输入格式】

第一行是数据组数 T。接下来 T 行表示 T 段听写的内容,

注意一段听写内容中可能有多句话, 但句与句之间没有标点。

【输出格式】

对于每组数据,输出一或两行。如果当前这段听写内容出现了问题,

那么输出“Yes”,否则输出“No” ,并在接下来的一行里输出这段话中从句的总个数。

【样例输入】

  • 3
  • zwz
  • zwbzw
  • zwbwb

【样例输出】

  • Yes
  • Yes
  • No
  • 1

【标签】找规律 + 分类讨论 + 模拟

貌似是独创方法???好像dalao和官方题解都不是这样写的来着...

难道是我终于聪明了一回???哈哈哈......qwq

方法就是,找规律,发现原串可以分成:

  • zwb wb w zwb w zwb w zwb w zwb w zw zwb wb wb wb wb w zwb
  • w zwb wb wb w zwb wb w zwb w zw zwb w zwb wb w zw zw zw zw zw
  • zw zwb wb w zwb wb wb w zw zwb (答案为 49)

为什么这样分?断开所有的 ‘ w ’。并且是按照规律断开。

【规律】如果是合法序列,‘ w ’ 的存在形态只可能是:

  •  w ; wb ; zw ; zwb 。

然后对应的分别构成 2, 1, 1, 0 个从句(按省略的z或b计算)。

这样只要特判一下不可能的情况,顺序扫一遍就行了....qwq。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;

//【听写】

//zwb wb w zwb w zwb w zwb w zwb w zw zwb wb wb wb wb w zwb 
//w zwb wb wb w zwb wb w zwb w zw zwb w zwb wb w zw zw zw zw zw 
//zw zwb wb w zwb wb wb w zw zwb

void reads(ll &x){ //读入优化(正负整数)
    ll fx=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=fx; //正负号
}

char ss[100019];

int main(){
    freopen("dictation.in","r",stdin);
    freopen("dictation.out","w",stdout);
    int n; scanf("%d",&n);
    for(int kase=1;kase<=n;kase++){
        scanf("%s",ss); int i=0,len=strlen(ss),ans=0,okk=true;
        if(ss[0]!='z'||ss[len-1]!='b')
            { printf("Yes\n"); continue; }
        while(i<len){
            if(ss[i]=='z'&&ss[i+1]=='w'&&ss[i+2]=='b') 
              {  i+=3; continue; }
            if(ss[i]=='z'&&ss[i+1]=='w'&&ss[i+2]=='w')
              {  printf("Yes\n"); okk=false; break; }
            if(ss[i]=='z'&&ss[i+1]=='w'&&ss[i+2]=='z') 
              {  i+=2; ans++; continue; } //填一个b
            if(ss[i]=='z') //z后面不是w,或者 zw 在末尾,都是不行的
              {  printf("Yes\n"); okk=false; break; }
            if(ss[i]=='w'&&ss[i+1]=='w') //两个w不能相连
              {  printf("Yes\n"); okk=false; break; }
            if(ss[i]=='w'&&ss[i+1]=='b') //wb + z/w..
              {  i+=2; ans++; continue; }
            if(ss[i]=='w'&&ss[i+1]=='z') //w + z..
              {  i++; ans+=2; continue; }
            if(ss[i]=='w') //单独的 w 在末尾,是不行的
              {  printf("Yes\n"); okk=false; break; }
            if(ss[i]=='b') //不可能存在单独的b
              {  printf("Yes\n"); okk=false; break; }
        } if(okk==true) cout<<"No"<<endl<<ans<<endl;
    }
}

【T2】遥远的金字塔(pyramid)

  • 在历史课上,你有一个金字塔的侧视图。这个金字塔一共有 n 层,
  • 从下至上编号为 1至 n。除了第一层外,其余层均被它底下的一层包含。
  • 即如果 xi,yi分别表示第 i 层的左、右端点,则对于任意的 i≥2,都有 xi≥xi-1,且 yi≤yi-1。
  • 每一层矩形的高度均为 1。 在整个侧视图中选择恰好 k 个不相交的矩形 ,
  • 问这 k 个不相交的矩形覆盖的面积最大是多少。

【输入格式】

第一行两个整数 n 和 k。第二至 n+1 行,

其中第 i+1 行两个整数 xi和 yi,分别表示第 i 层金字塔的左右端点。

【输出格式】

只包含一个整数,即最大覆盖面积。

【样例输入】

  • 5 3
  • 1 6
  • 1 5
  • 3 5
  • 4 4
  • 4 4

【样例输出】

  • 15

【标签】DP + 斜率优化 + 单调队列维护单调性

......普通 DP 80分,就是这个式子:

  • f [ i ][ j ] = max( f [ k ][ j - 1 ] + ( y[ i ] - x [ i ] ) * ( i - ( k + 1 ) + 1 ) ) ;

然后我写斜率优化坑了好久...结果只加上了15分...

好吧我确实是傻逼...要化除为乘才能AC2333...

具体分析过程看代码吧 ovo 注释超详细的 qwq

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;

//【遥远的金字塔】

//f[i][j]表示已经到第i层(从下到上),已经选了j个矩形的max面积和。

//枚举包含i的矩形的最下层(编号小)的下一层k=( j-1 ~ i-1 )
//为什么从j-1开始?因为如果一层选两个矩形,一定不是最优。

//f[i][j]=max(f[k][j-1]+(y[i]-x[i])*(i-(k+1)+1));
//因为金字塔满足包含关系,所以最短的一定是最上层的y[i]-x[i]。

//枚举k肯定会超时啦啦啦...所以尝试一下单调队列优化?

//f[i][j]=max(f[k][j-1]+a[i]*i-a[i]*k);

//f[i][j]=max(f[k][j-1]-a[i]*k) + a[i]*i;

//暂时把j这一维省略掉...f[i]=max(f[k]-a[i]*k)+a[i]*i;

//这东西好眼熟...有点像...斜率优化?

//yi-xi*k>=yk-xk*k (k=a[i])

//a[i]<=f[i]-f[k]/(i-k);我真的忘了斜率优化怎么做的了...

//slop(i,k)=f[i]-f[k]/(i-k)?然后用单调队列...?

//维护队头的可行性(对于每个新的i),队尾的单调性...队头即为最优解。

void reads(ll &x){ //读入优化(正负整数)
    ll fx=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=fx; //正负号
}

ll f[20019][109],q[109][20019],a[20019],head[109],tail[109];

double slop(ll k1,ll k2,ll j){ return (double)(f[k2][j]-f[k1][j]); }

int main(){
    freopen("road.in","r",stdin);
    freopen("pyramid.out","w",stdout);
    ll n,m,x,y,ans=0; reads(n),reads(m);
    
    for(ll i=1;i<=n;i++) reads(x),reads(y),a[i]=y-x+1;

    for(ll j=0;j<=m;j++) head[j]=1,tail[j]=1,q[j][1]=0;

    for(ll i=1;i<=n;i++)
        for(ll j=min(i-1,m-1);j>=0;j--){ //↓↓维护队头可行性
        
            while(head[j]<tail[j]
                &&slop(q[j][head[j]],q[j][head[j]+1],j)
                    >(double)a[i]*(q[j][head[j]+1]-q[j][head[j]])) head[j]++;
            
            f[i][j+1]=f[q[j][head[j]]][j]-a[i]*q[j][head[j]]+a[i]*i; 
            //(q[j][head[j]]+1)~i行组成矩形,答案最优
            
            while(head[j+1]<tail[j+1]
                &&slop(q[j+1][tail[j+1]-1],q[j+1][tail[j+1]],j+1)*(i-q[j+1][tail[j+1]])
                    <slop(q[j+1][tail[j+1]],i,j+1)
                        *(q[j+1][tail[j+1]]-q[j+1][tail[j+1]-1])) tail[j+1]--;

            q[j+1][++tail[j+1]]=i; //↑↑↑需要将i入队,但要保持队列斜率单调性
        }

    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=m;j++)
            if(ans<f[i][j]) ans=max(ans,f[i][j]);
    
    cout<<ans<<endl; return 0;
}


【T3】心灵治愈(heal)

  • “假设这里有一条长轴,在 0 号位置有一个小球,它代表一个人来临到这个世界,
  • 开始了他的人生道路。他将很多个人生境况,每个境况对人生可能产生多次影响。
  • 而对于每一次影响,它也许能帮助你前进,但如果你不能保持成功不骄傲,
  • 失败不气馁,那表面看起来的好的境况也许会让你后退,因此一个人有可能会走到负数。
  • “而大部分境况的影响值是由你后天所决定的。如果你能好好认识与对待境况,
  • 它们就会让你前进相应的影响值。否则,你可能会后退相应的影响值,或者待在原点。
  • “但是人类无法摆脱贪婪的特点,他们始终尽可能的前进。 但是,大家似乎都忽略了一点,
  • 那就是人生的终点其实就在 1 号点。许许多多的人由于境况影响值的原因,无法到达生命的
  • 终点,那他的一生就是不圆满的。回到原点的前面,这大概就是生命的真谛吧。 ”羽点点头。
  • “在生命终点的境况的影响永远是最大的,它之前没有哪一个境况的影响值是大于它的。
  • 因为在生命终点浮现在你脑海里的永远是对你最重要的东西。
  • 并且生命结束的方式和其影响值其实在你出生的时候就已经决定了。
  • “那么从人生的起点到终点,成长羁绊的境况的影响值组合究竟有多少种情况呢?”

【输入格式】

输入文件有且仅有一行,包括用空格分开的两个整数 N 和 M。

N 表示一共有 N+1 个人生境况,M 表示生命终点的境况的影响值。

【输出格式】

输出有多少种境况的影响值的组合能满足可以从人生起点走到终点。

由于答案可能很大,输出它模 1000000007的值。注意,境况是按时间有序的。

【样例输入1】

  • 2 3

【样例输出1】

  • 8

【样例输入2】

  • 8 8

【样例输出 2】

  • 16711680

【样例说明】

为了不影响【题目描述】的优美性,题意的进一步解释将放在【样例说明】中。

1.境况的总个数已经确定为 N+1,除了第 N+1 个境况,其他境况的影响值未知;

2.长轴表示人生的道路,每一个点对应人生的一个位置,长轴无限长,

但长轴与境况没有关系,即任一境况可以在任意的位置发生,位置对境况没有任何影响;

3.生命结束的方式,即第 N+1 个境况,它的影响值已经在出生时就给定为 M;

4.其他境况(第 1-N 个境况)的影响值不会大于第 N+1 个境况的影响值,

且其影响值一定是正的, 但在长轴上的效果是可以是负的,

后退即在当前位置减去这个境况的影响值到达新的位置;

5.每一个境况都会影响人生至少一次,同一个境况可以影响人生多次,

并且每一次对人生的影响效果(前进 或 后退 或 停在原地)可以不同;

6.长轴的点不一定都会经过,每一个境况会依次经过;

7.题目要求的是有多少种境况影响值的组合,使得在经历了这些境况之后,

可以从生命起点到达生命的终点,境况具体怎么影响人生可参见下面的解释;

8.境况的影响值组合是一个长度为 N+1 的序列,第 i 个数代表第 i 个境况的影响值

(不需要输出) ,注意:境况的影响值并不是该境况对人生产生影响的次数,

而是每一次该境况对人生产生影响效果时前进或后退的距离 。

9.境况是按时间有序的,即同样的组合,不同的顺序也分别计入答案。

对于样例 1,这 8 种可能的 境况影响值组合 分别是:

(1, 1, 3), (1, 2, 3), (1, 3, 3), (2, 1, 3), (2, 2, 3), (2, 3, 3), (3, 1, 3), (3, 2, 3).


【标签】题面理解 + 大胆猜想 + 质因数 +容斥原理

题意转化为:a[n+1]=m,a[1~n]<m,求所有的数的最大公因数是1的方案数。

...一开始算错了4^8...然后发现 8 8 真的就是 8^8-4^8...

(1)总数m^n。分解m=p1^k1*p2^k2*...*pt^kt,

        那么选择的数不能全部都是pi的倍数:- (m/pi)^n。

        但重复减掉了完全是pi*pj的倍数的情况(一共两个质因子)。

(2)如果有两个因子相乘的倍数pi*pj,+ (m/(pi*pj))^n。

(3)如果有三个因子相乘的倍数pi*pj*pk,

        先被减了3次,又被两两组合,加了3*(3-1)/2=3次。

        那么还要 - (m/(pi*pj*pk))^n。

(4)如果有四个因子相乘的倍数pi*pj*pk*pl,

        先被减了4次,又被两两组合加了4*(4-1)/2=6次,

        还被三三组合减了4次,那么还要再加一次 + (m/(pi*pj*pk*pl))^n。

(5)如果有五个因子相乘的倍数pi*pj*pk*pl*pt,

        先被减了5次,又被两两组合加了5*(5-1)/2=10次,

        被三三组合减了10次,被四四组合加了5次,

        还要减一次 - (m/(pi*pj*pk*pl*pt))^n

然后就发现了规律...不过好像容斥原理里面本来就有这种定理吧??

质因子数相乘<10^15,所以质因子个数不会超过15个...

dfs就行...(我才不要说这个dfs是抄的 LRZ大神 的 qaq)

大概我能看懂题意,是因为我语文比较好吧???

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;

//【心灵治愈】

//a[n+1]=m,a[1~n]<m,求所有的数的最大公因数是1的方案数

//...一开始算错了4^8...然后发现 8 8 真的就是 8^8-4^8...

//总数m^n。分解m=p1^k1*p2^k2*...*pt^kt,
//那么这些数不能完全是pi的倍数:-(m/pi)^n。
//但重复减掉了完全是p1*p2的倍数的情况(一共两个质因子)。

//如果有两个因子相乘的倍数pi*pj,+(m/(pi*pj))^n。

//如果有三个因子相乘的倍数pi*pj*pk,
//先被减了3次,又被两两组合,加了3*(3-1)/2=3次。
//那么还要 -(m/(pi*pj*pk))^n。

//如果有四个因子相乘的倍数pi*pj*pk*pl,
//先被减了4次,又被两两组合加了4*(4-1)/2=6次,
//还被三三组合减了4次,那么还要再加一次 +(m/(pi*pj*pk*pl))^n。

//如果有五个因子相乘的倍数pi*pj*pk*pl*pt,
//先被减了5次,又被两两组合加了5*(5-1)/2=10次,
//被三三组合减了10次,被四四组合加了5次,
//还要减一次 -(m/(pi*pj*pk*pl*pt))^n。

//然后就发现了规律...质因子数相乘<10^15,应该不会超过20个吧...

void reads(ll &x){ //读入优化(正负整数)
    ll fx=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=fx; //正负号
}

const ll mod=1000000007;

ll primes[50019],cnt=0,n,m,ans; //质因子

void get_prime(ll x){ //找到x的所有质因子
    for(int i=2;i<=sqrt(x+1);i++){
        if(x%i!=0) continue;
        primes[++cnt]=i; //记录质因子
        while(x%i==0) x/=i; //除掉所有i
    } if(x!=1) primes[++cnt]=x;
}

ll power(ll a,ll b){ //ksm
    ll anss=1; a=a%mod;
    while(b>0){
        if(b&1) anss=(anss*a)%mod;
        a=a*a%mod; b>>=1;
    } return anss;
}

void dfs(ll flag,ll now_cnt,ll now_sum){
    if(now_cnt==cnt+1){ //递归的最底层
        if(flag&1) ans=(ans-power(m/now_sum,n)+mod)%mod;
        else ans=(ans+power(m/now_sum,n))%mod;
        return; //判断2^cnt种选择方案之一的情况、加或减的对答案的影响
    } dfs(flag^1,now_cnt+1,now_sum*primes[now_cnt]); 
    dfs(flag,now_cnt+1,now_sum); //每个因子可以选择乘上或者不乘
}

int main(){
    freopen("heal.in","r",stdin);
    freopen("heal.out","w",stdout);
    reads(n),reads(m),get_prime(m);
    dfs(0,1,1); //flag,当前枚举到的因子数,当前凑成的因子积 
    //flag中的0表示此层为奇数层,需要减,1表示此层为偶数层,需要加
    cout<<ans<<endl; return 0;
}

 

 

好了题目都分析完了...再次怀念一下AK的故事qwq

 

                                   ——时间划过风的轨迹,那个少年,还在等你。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOIP(全国青少年信息学奥林匹克竞赛)是中国国内最高水平的信息学竞赛之一,是计算机领域的重要赛事。针对NOIP模拟题,通常是为了帮助参赛者熟悉比赛形式和题型,提供足够的训练机会。 数据下载是NOIP比赛中的一个重要环节,因为参赛者需要根据提供的数据集进行程序开发和测试。数据下载一般通过网络进行,参赛者需要在指定的时间段内下载数据集到自己的计算机上。 在进行NOIP模拟题数据下载时,我们可以按照以下步骤进行操作: 1. 确认下载链接:NOIP官方会提供下载数据集的链接或者FTP地址,参赛者需要确认链接是否可用和正确。 2. 选择下载工具:根据自己的需求,参赛者可以选择合适的下载工具进行操作。常见的下载工具有浏览器内置下载工具、迅雷、IDM等,可以根据个人的习惯和需求选择合适的下载工具。 3. 打开下载工具:根据所选择的下载工具类型,打开对应的软件,进入下载界面。 4. 输入下载链接:将NOIP提供的数据集下载链接复制粘贴到下载工具的链接输入框中,点击确定或开始进行下载。 5. 等待下载完成:根据数据集的大小和网络速度不同,下载时间会有所变化。参赛者需要耐心等待下载完成,确保数据集完整地保存到自己的计算机上。 6. 验证数据完整性:下载完成后,参赛者需要验证数据集的完整性,确保所有文件都成功地保存到指定位置。可以进行文件大小的比对或者逐个文件的校验来检查数据完整性。 通过以上步骤,参赛者可以成功地进行NOIP模拟题数据的下载。在实际比赛中,一个高效的数据下载过程可以提高参赛者的准备时间和竞争力,确保能够充分利用所提供的数据集进行开发和测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值