NOIP模拟赛(10.17):语言,色球,斐波,偶数
语言
题面:
牛妹正在学习一种新的语言,在这种语言里,单词只有形容词(
一个名词性词组(
一个句子(
牛妹用这个语言写下一个单词的序列,现在你想知道这个单词序列是否能通过适当安排序列里每个单词的词性使之成为一个句子(不同位置的相同的单词也可以安排不同的词性)。
为了简单起见,她把每个单词编码对应为一个小写拉丁字母,不同的单词对应不同的字母(这里我们假设序列里面不同的单词的总数不超过
具体的对应关系如下表所示:
编码 | 词性 |
---|---|
题解:
做法 ( pts):
注意到一个句子中动词有且仅有一个,所以对于类型4单词数大于1的情况,直接输出NO即可
根据题意,只要是一个不包含动词且以名词结尾的字符串则一定可以视为一个名词词组(即
发现动词前必须为名词类型,结尾必须为名词类型
所以枚举动词位置,判断动词前及句末是否有名词即可,若存在类型4且数量为一,则动词一定为类型4单词
Code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int w[26],a[N],n,cnt,pos;
string s1;
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
// freopen("language.in","r",stdin);
// freopen("language.out","w",stdout);
int t;
cin>>t;
while(t--){
cnt=0,pos=0;
for(int i=0;i<26;i++)cin>>w[i];
cin>>s1;
n=s1.length();
for(int i=1;i<=n;i++){
a[i]=w[s1[i-1]-'a'];
if(a[i]==4)cnt++,pos=i;//记录类型4单词数量
}
if(a[n]==1||a[n]==4||a[n]==5||cnt>1){
cout<<"No\n";
continue;
}
if(cnt==1){
if(a[pos-1]==0||a[pos-1]==1||a[pos-1]==5){
cout<<"No\n";
continue;
}else {
cout<<"Yes\n";
continue;
}
}
bool suc=1;
for(int i=2;i<n;i++){
if((a[i]==5||a[i]==6||a[i]==7)&&(a[i-1]==2||a[i-1]==3||a[i-1]==6||a[i-1]==7)){
cout<<"Yes\n";//枚举动词位置
suc=0;
break;
}
}
if(suc)cout<<"No\n";
}
return 0;
}
色球
题面:
牛牛有
一开始这些球桶都是空的,紧接着他会依次做
push x y z
,表示把 个颜色为 的彩色小球放到第 个桶的最上面;pop x z
,表示把最上面的 个小球从第 个桶内拿出来;put u v
,表示把第 个桶的所有小球依次从顶部拿出放入第 个桶内。
现在他已经确定好了这
题解:
做法( pts):
fhq平衡树维护序列,代码难度较大(考场上写两个半小时,还是炸到70分),序列翻转,删除,合并都是经典操作。文艺平衡树
Code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4002000;
int n,m,root[N],tot;
namespace treap{
struct fhq_treap{
int ls,rs,col,val,rev;
ll sz,num;
}tr[N];
void init(){
tr[0].ls=0;
tr[0].rs=0;
tr[0].val=0;
tr[0].num=0;
tr[0].sz=0;
tr[0].col=0;
tr[0].rev=0;
}
int newnode(int col,ll num){
++tot;
tr[tot].val=rand()*rand();
tr[tot].col=col,tr[tot].sz=tr[tot].num=num;
tr[tot].ls=tr[tot].rs=0;
tr[tot].rev=0;
return tot;
}
void pushup(int rt){
tr[rt].sz=tr[rt].num;
if(tr[rt].ls){
tr[rt].sz+=tr[tr[rt].ls].sz;
}
if(tr[rt].rs){
tr[rt].sz+=tr[tr[rt].rs].sz;
}
}
void pushdown(int rt){
if(tr[rt].rev){
if(tr[rt].ls){
tr[tr[rt].ls].rev^=1,swap(tr[tr[rt].ls].ls,tr[tr[rt].ls].rs);
}
if(tr[rt].rs){
tr[tr[rt].rs].rev^=1,swap(tr[tr[rt].rs].ls,tr[tr[rt].rs].rs);
}
tr[rt].rev=0;
}
}
int merge(int x,int y){
if((!x)||(!y)){
return x|y;
}
pushdown(x),pushdown(y);
int rt;
if(tr[x].val<tr[y].val){
rt=x;
tr[rt].rs=merge(tr[x].rs,y);
}
else{
rt=y;
tr[rt].ls=merge(x,tr[y].ls);
}
pushup(rt);
return rt;
}
void split(int rt,ll k,int &x,int &y,int &z){
if(!rt){
x=y=z=0;
return;
}
pushdown(rt);
if(tr[tr[rt].rs].sz>=k){
x=rt;
split(tr[rt].rs,k,tr[rt].rs,y,z);
}
else if(tr[tr[rt].rs].sz+tr[rt].num<k){
z=rt;
split(tr[rt].ls,k-tr[tr[rt].rs].sz-tr[rt].num,x,y,tr[rt].ls);
}
else{
x=tr[rt].ls,z=tr[rt].rs;
y=rt;
tr[rt].ls=tr[rt].rs=0;
}
pushup(rt);
}
void pop_nd(int x,int k){
int a,b,c;
split(root[x],k,a,b,c);
root[x]=merge(a,newnode(tr[b].col,tr[c].sz+tr[b].num-k));
cout<<tr[b].col<<endl;
}
}
using namespace treap;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);
string s;
cin>>n>>m;
for(int cas=1;cas<=m;++cas){
cin>>s;
int x,y,z;
if(s=="push"){
cin>>x>>y>>z;
root[z]=merge(root[z],newnode(y,x));
}
else if(s=="put"){
cin>>x>>y;
tr[root[x]].rev^=1;
swap(tr[root[x]].ls,tr[root[x]].rs);
root[y]=merge(root[y],root[x]);
root[x]=0;
}else{
cin>>x>>y;
pop_nd(y,x);
}
init();
}
return 0;
}
做法( pts):
每个位置用双向链表顺序记录放的球的个数和颜色。
对于询问
对于询问
对于询问
代码难度相对较小。
Code:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 301001;
struct node {
int c,num,ch[2];
} buf[maxn];
int n,m,h[maxn],t[maxn],tot;//h:链表头 t:链表尾
int main() {
freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=0; i<m; i++) {
char op[6];
int x,y,z;
scanf("%s%d%d",op,&x,&y);
if (op[2]=='s') {
scanf("%d",&z);
buf[++tot]=(node){y,x,h[z],0};
if (h[z])
buf[h[z]].ch[0]?(buf[h[z]].ch[1]=tot):(buf[h[z]].ch[0]=tot);//将链表空的一头连接新点
else t[z]=tot;//若链表为空,将该点作为链表尾
h[z]=tot;//更新链表头
}
if (op[2]=='p') {
while (buf[h[y]].num<x) {
x-=buf[h[y]].num;
int lch=buf[h[y]].ch[0]|buf[h[y]].ch[1];//获取链表头的下一个节点
if (lch)buf[lch].ch[0]==h[y]?(buf[lch].ch[0]=0):(buf[lch].ch[1]=0);//删除链表头
h[y]=lch;//更新链表头
}
buf[h[y]].num-=x//减去剩余的
printf("%d\n",buf[h[y]].c);
}
if(op[2]=='t'){
if (!h[x]) continue;//若链表x为空,跳过该操作
if (h[y]) {//完成链表头的连接
buf[h[y]].ch[0]?(buf[h[y]].ch[1]=h[x]):(buf[h[y]].ch[0]=h[x]);//将y链表头空的一头连向x的链表头
buf[h[x]].ch[0]?(buf[h[x]].ch[1]=h[y]):(buf[h[x]].ch[0]=h[y]);//将x链表头空的一头连向y的链表头
} else {
t[y]=h[x]; //若链表y为空,则直接将用x链表头作为y的尾部
}
h[y]=t[x];//更新y链表头
h[x]=t[x]=0;//清空x链表
}
}
return 0;
}
偶数
题面:
牛牛喜欢偶数,他定义一种新的『偶数』为:数字的位数(在十进制下,去掉前导零)为偶数,且数字前一半和后一半完全一致。比如
对于一个『偶数』,牛牛可以在这个『偶数』后继续添加数字,使得它成为新的『偶数』。比如,
对于任何一个『偶数』,牛牛都可以通过上述的方式产生新的『偶数』,这个新的『偶数』继续产生下一个新的『偶数』,直到这个『偶数』的位数超过任意给定的正整数
题解:
做法( pts):
假设𝑢是𝑣𝑣,那么新的“偶数”必然是𝑣𝑤𝑣𝑤的形式,其中𝑤是𝑣的一个前缀,且是 𝑣的一个周期。可以证明𝑤应该取𝑣最小的周期。因此使用KMP求𝑣的周期,每
次得到𝑤后,新的𝑣𝑤作为𝑣继续求周期,直到𝑣的长度超过𝑛为止。
询问时预处理
做法( pts):
设
是 的最短周期,且 不是 的因子,则 的最短的周期是
证明:假设
如果
如果
所以我们只需要预处理
Code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int N=1e5+10;
const int M=100;
#define int long long
char s[N];
int leng,lim,n,q,l,r;
int nxt[N],len[M],f[M],fs[N],base[M];
//快速幂
int qpow(int a,int b){
int res=1;
while(b>0){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res%mod;
}
//KMP
void get_nxt(){
nxt[1]=0;
for(int i=2,j=0;i<=leng;i++){
while(j&&s[j+1]!=s[i])j=nxt[j];
if(s[j+1]==s[i])j++;
nxt[i]=j;
}
}
//倍增
int get_ans(int r){
int sum=0,ans=0;
for(int i=lim;i>=0;i--){
if(sum+len[i]<=r){
sum+=len[i];
ans=(ans*base[i]%mod+f[i])%mod;
}
}
return (ans*qpow(10,r-sum)%mod+fs[r-sum])%mod;//处理剩余部分
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
freopen("code.in","r",stdin);
int t;
cin>>t;
while(t--){
cin>>s+1>>n>>q;
leng=strlen(s+1)/2;
for(int i=1;i<=leng;i++)fs[i]=(fs[i-1]*10LL+s[i]-'0')%mod;//预处理原字符串对应数字
get_nxt();
len[0]=leng-nxt[leng],len[1]=leng;
f[0]=fs[len[0]],f[1]=fs[len[1]];
lim=1;
//倍增
for(int i=2;i<=100;i++){
len[i]=len[i-1]+len[i-2];
f[i]=(f[i-1]*qpow(10,len[i-2])%mod+f[i-2])%mod;
if(len[i]>=n){
lim=i;
break;
}
}
for(int i=0;i<=lim;i++)base[i]=qpow(10,len[i]);//预处理10的幂
for(int i=1;i<=q;i++){
cin>>l>>r;
//截取l-r的数字
cout<<(get_ans(r)-get_ans(l-1)*qpow(10,r-l+1)%mod+mod)%mod<<"\n";
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架