CSP2024 前集训:多校A层冲刺NOIP2024模拟赛04

news/2024/10/10 9:50:57/文章来源:https://www.cnblogs.com/Charlieljk/p/18455671

前言

image

T1 签了。

T2 一眼后缀数组板子,但是复杂度是 \(O(nq\log(n))\) 的,极限数据本地 \(4\) 秒,但如果您会 \(O(n)\) 求后缀数组的话就直接过掉了,但赛时数据貌似纯随机,遂可以直接过掉,可以优化成 \(O(n^2\log(n)+nq)\)\(O(n^2\log(n)+q)\) 的,赛时想打这个但是怕常熟大和上面区别不大,遂没打,实际上第二种快得多,极限数据本地 \(500ms\),正常评测姬都能过。

T3 想到建两棵 trie 树但是细节没想出来。

T4 想到对模数讨论,但不会 crt 遂根本没往那边想。

T1 02表示法

高精度加二进制分解板子,递归即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=610;
template<typename Tp> inline void read(Tp&x)
{x=0;register bool z=true;register char c=getchar_unlocked();for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
string s; int tot,a[N],cnt[N],b[N][20];
bool big_than_0(string &s) {return s.size()>1||(s.size()==1&&s.back()!='0');}
void chu_2(string &s)
{reverse(s.begin(),s.end());for(int i=s.size()-1,now=0;i>=0;i--){int x=s[i]-'0'+now*10,tmp=x>>1;now=x-(tmp<<1),s[i]=tmp+'0';}while(s.size()>1&&s.back()-'0'==0) s.pop_back();reverse(s.begin(),s.end());
}
void output(int x)
{putchar_unlocked('2');if(x==1) return ; putchar_unlocked('(');if(x==0) return putchar_unlocked('0'),putchar_unlocked(')'),void();memset(b[x],0,sizeof(b[x])),cnt[x]=0;for(int i=0,y=x;y;i++,y>>=1) if(y&1) b[x][++cnt[x]]=i;reverse(b[x]+1,b[x]+1+cnt[x]);for(int i=1;i<=cnt[x];i++){output(b[x][i]);if(i!=cnt[x]) putchar_unlocked('+');}putchar_unlocked(')');
}
signed main()
{freopen("pow.in","r",stdin),freopen("pow.out","w",stdout);cin>>s;for(int i=0;big_than_0(s);i++,chu_2(s))if((s.back()-'0')&1) a[++tot]=i;reverse(a+1,a+1+tot);for(int i=1;i<=tot;i++) {output(a[i]); if(i!=tot) putchar_unlocked('+');}
}

T2 子串的子串

  • 部分分 \(100pts\):每次查询都重构一遍后缀数组,最后答案为 \(\dfrac{len\times (len+1)}{2}-\sum\limits_{i=l}^r{height_i}\)\(O(nq\log(n))\)

  • 正解一:

    考虑 \(q\) 很大 \(n\) 很小,所以会存在大量右端点重复的,所以总共只需要跑 \(n\) 遍后缀数组,对于右端点相同根据 \(lcp(sa_l,sa_r)=\min\limits_{i=l+1}^r{height_i}\),套个 ST 表即可。

    可以将左端点排序套链表做,也可以每个询问都 \(O(n)\) 跑一遍,前者复杂度为 \(O(n^2\log(n)+q)\),后者为 \(O(n^2\log(n)+nq)\),排序可以用桶排,后者就能过。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long 
    #define endl '\n'
    #define sort stable_sort
    #define pb push_back
    using namespace std;
    const int N=3010,M=2e4+10;
    template<typename Tp> inline void read(Tp&x)
    {x=0;register bool z=true;register char c=getchar_unlocked();for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);x=(z?x:~x+1);
    }
    template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
    template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
    template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
    template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
    int n,m,mx[N],sa[N],rk[N],id[N],pos[N],cnt[N],key[N],ans[M],oldrk[N],height[N],mi[15][N];
    char s[N],t[N];
    struct aa {int l,id;}; vector<aa>e[N];
    void count_sort(int len,int m)
    {memset(cnt,0,4*(m+1));for(int i=1;i<=len;i++) cnt[key[i]]++;for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];for(int i=len;i>=1;i--) sa[cnt[key[i]]]=id[i],cnt[key[i]]--;
    }
    void init(char s[],int len)
    {int m=127,tot=0,num=0;for(int i=1;i<=len;i++) key[i]=rk[id[i]=i]=(int)s[i];count_sort(len,m);for(int w=1;w<len&&tot!=len;w<<=1,m=tot){num=0;for(int i=len;i>=len-w+1;i--) id[++num]=i;for(int i=1;i<=len;i++) if(sa[i]>w) id[++num]=sa[i]-w;for(int i=1;i<=len;i++) key[i]=rk[id[i]];count_sort(len,m); for(int i=1;i<=len;i++) oldrk[i]=rk[i];tot=0; for(int i=1;i<=len;i++){tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]);rk[sa[i]]=tot;}}for(int i=1,k=0;i<=len;i++) if(rk[i]){for(k-=(!!k);s[i+k]==s[sa[rk[i]-1]+k];k++);height[rk[i]]=k;}memset(mi,0x3f,sizeof(mi)),memset(cnt,0,sizeof(cnt));for(int i=1;i<=len;i++) mi[0][i]=height[i];for(int i=1;i<=__lg(len);i++) for(int j=1;j<=len;j++)mi[i][j]=min(mi[i-1][j],mi[i-1][j+(1<<(i-1))]);
    }
    int lcp(int l,int r)
    {l++; int t=__lg(r-l+1); return min(mi[t][l],mi[t][r-(1<<t)+1]);}
    signed main()
    {freopen("substring.in","r",stdin),freopen("substring.out","w",stdout);read(n,m),scanf("%s",s+1); for(int i=1;i<=n;i++) mx[i]=i+1;for(int i=1,l,r;i<=m;i++) read(l,r),e[r].pb((aa){l,i}),mx[r]=min(mx[r],l);for(int r=1;r<=n;r++){memset(t,0,sizeof(t));for(int i=mx[r];i<=r;i++) t[i-mx[r]+1]=s[i];init(t,r-mx[r]+1);for(auto x:e[r]){int l=x.l,id=x.id,tot=0; ans[id]=(r-l+1)*(r-l+2)>>1;for(int i=l-mx[r]+1;i<=r-mx[r]+1;i++) pos[++tot]=rk[i];for(int i=1;i<=tot;i++) cnt[pos[i]]++; tot=0;for(int i=1;i<=r-mx[r]+1;i++) for(;cnt[i];cnt[pos[++tot]=i]--);for(int i=2;i<=tot;i++) ans[id]-=lcp(pos[i-1],pos[i]);}}for(int i=1;i<=m;i++) write(ans[i]),puts("");
    }
    
  • 正解二(官方正解):

    统计每个 \([l,r]\) 对答案的贡献,\(O(n^2)\) 预处理,\(O(1)\) 查询。

    对于每个 \([l,r]\),计算其哈希值,会有重复的,所以 \(ans_{l,r}+1,ans_{last_{hash(l,r)},r}-1\),最后二维前缀和一下即可。

    复杂度 \(O(n^2+q)\) 甚至没有后缀数组跑得快。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define ull unsigned long long
    #define endl '\n'
    #define sort stable_sort
    using namespace std;
    const int N=3010,B=29;
    template<typename Tp> inline void read(Tp&x)
    {x=0;register bool z=true;register char c=getchar_unlocked();for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);x=(z?x:~x+1);
    }
    template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
    template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
    template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
    template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
    int n,m,ans[N][N]; char s[N]; 
    unordered_map<ull,int>last; ull tmp,b[N],h[N];
    void get_hash(char s[])
    {b[0]=1; for(int i=1;i<=n;i++) b[i]=b[i-1]*B;for(int i=1;i<=n;i++) h[i]=h[i-1]*B+(s[i]-'a');
    }
    ull ask(int l,int r) {return h[r]-h[l-1]*b[r-l+1];}
    signed main()
    {freopen("substring.in","r",stdin),freopen("substring.out","w",stdout);read(n,m),scanf("%s",s+1),get_hash(s);for(int i=1,vr=n;i<=n;i++,vr--,last.clear())for(int l=1,r=i;r<=n;l++,r++)ans[last[tmp=ask(l,r)]][r]--,ans[l][r]++,last[tmp]=l;for(int l=n;l>=1;l--) for(int r=l;r<=n;r++)ans[l][r]+=ans[l+1][r]+ans[l][r-1]-ans[l+1][r-1];for(int i=1,l,r;i<=m;i++) read(l,r),write(ans[l][r]),puts("");
    }
    

T3 魔法咒语

建两棵trie 树,原串建一棵,反串建一棵。

那么在原树上遍历到节点 \(p\) 时,若他没有 \(c\) 儿子,就将反树中 \(c\) 的贡献加上。

这样会有两种情况考虑不到,一是原串可以被其自身前 \(len-1\) 个字符和最后一个字符拼出来,一是只有一个字符的串不可能被拼出来,特殊处理一下即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e4+10,M=4e5+10;
template<typename Tp> inline void read(Tp&x)
{x=0;register bool z=true;register char c=getchar_unlocked();for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n; ll ans; bool vis[26],yet[26];
struct trie
{int tot,son[M][26],cnt[26];void insert(char s[],int len,bool d){if(d) reverse(s,s+len);for(int i=0,p=0,c;i<len;i++){if(!son[p][c=s[i]-'a']) son[p][c]=++tot,cnt[c]++;p=son[p][c];}}
}pre,suf;
signed main()
{freopen("magic.in","r",stdin),freopen("magic.out","w",stdout);read(n); char s[45];for(int i=1,len;i<=n;i++) {scanf("%s",s),len=strlen(s),vis[s[len-1]-'a']=1;pre.insert(s,len,0),suf.insert(s,len,1);if(len==1&&!yet[s[0]-'a']) ans+=(yet[s[0]-'a']=1);}for(int p=1;p<=pre.tot;p++) for(int c=0;c<26;c++)(!pre.son[p][c])?ans+=suf.cnt[c]:ans+=vis[c];write(ans);
}

T4 表达式

对于模数很小的情况,我们可以建一棵线段树,\(val_{p,x}\) 表示在线段树节点 \(p\) 管辖区间内,输入的树是 \(x\) 的结果,显然有 pushup 为 \(val_{p,x}=val_{re,val_{ls,x}}\),即先放到左边跑,再放到右边跑,叶子结点特殊处理。

那么对于模数很大的,发现数据中模数都是合数或很小的质数(前三个点暴力除外),考虑将其分解成几个较小的数的乘积,对于每个分解出的数跑线段树,最后用 crt 合并答案即可。

实现细节上,需要扩展欧拉定理,不然快速幂指数太大复杂度爆炸,所以要求出每个分解出的数的欧拉函数,由此求逆元又可以用快速幂了,不需要 exgcd,这样快速幂的 \(\log\) 直接变成常数级别,跑得飞快。

甚至为了做这题专门学了 crt 和扩欧。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{x=0;register bool z=true;register char c=getchar_unlocked();for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int _,n,m,P,a[N],mod[5],phi[5],inv[5],val[N<<2][5][30]; char s[N];
ll qpow(ll a,int b)
{ll res=1; for(;b;(a*=a)%=P,b>>=1) if(b&1) (res*=a)%=P; return res;}
int qpow(int a,int b,int p)
{int res=1; for(;b;(a*=a)%=p,b>>=1) if(b&1) (res*=a)%=p; return res;}
void pushup(int p)
{for(int i=1;i<=mod[0];i++) for(int j=0;j<mod[i];j++)val[p][i][j]=val[rs][i][val[ls][i][j]];
}
void calc(int p,int x)
{if(s[x]=='+') for(int i=1,tmp;i<=mod[0];i++){tmp=a[x]%mod[i];for(int j=0;j<mod[i];j++) val[p][i][j]=(j+tmp)%mod[i];}else if(s[x]=='*') for(int i=1,tmp;i<=mod[0];i++){tmp=a[x]%mod[i];for(int j=0;j<mod[i];j++) val[p][i][j]=j*tmp%mod[i];}else for(int i=1,tmp;i<=mod[0];i++){tmp=a[x]?a[x]%phi[i]+phi[i]:0;for(int j=0;j<mod[i];j++) val[p][i][j]=qpow(j,tmp,mod[i]);}
}
void build(int p,int l,int r)
{if(l==r) return calc(p,l),void();build(ls,l,mid),build(rs,mid+1,r),pushup(p);
}
void change(int p,int l,int r,int x)
{if(l==r) return calc(p,l),void();x<=mid?change(ls,l,mid,x):change(rs,mid+1,r,x),pushup(p);
}
signed main()
{freopen("expr.in","r",stdin),freopen("expr.out","w",stdout);read(_,n,m,P); for(int i=1;i<=n;i++) {while(s[i]!='+'&&s[i]!='*'&&s[i]!='^') s[i]=getchar_unlocked();read(a[i]);}if(_<=3){ll x; for(int i=1,op;i<=m;i++){read(op,x);if(op&1){for(int j=1;j<=n;j++)s[j]=='+'?(x+=a[j])%=P:(s[j]=='*'?(x*=a[j])%=P:x=qpow(x,a[j]));write(x),puts("");}else{for(s[x]=0;s[x]!='+'&&s[x]!='*'&&s[x]!='^';) s[x]=getchar_unlocked();read(a[x]);}}return 0;}int x=P; for(int i=2,tmp=sqrt(P),sum;i<=tmp;i++) if(!(x%i)){for(sum=1;!(x%i);x/=i) sum*=i;mod[++mod[0]]=sum,phi[mod[0]]=sum/i*(i-1);}if(x>1) mod[++mod[0]]=x,phi[mod[0]]=x-1;for(int i=1;i<=mod[0];i++) inv[i]=qpow(P/mod[i],phi[i]-1,mod[i]);build(1,1,n); for(int i=1,op,x;i<=m;i++){read(op,x);if(op&1){int res=0; for(int j=1;j<=mod[0];j++)(res+=P/mod[j]*inv[j]%P*val[1][j][x%mod[j]]%P)%=P;write(res),puts("");}else{for(s[x]=0;s[x]!='+'&&s[x]!='*'&&s[x]!='^';) s[x]=getchar_unlocked();read(a[x]),change(1,1,n,x);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/810939.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

抽象函数中图象变换的应用

一 问题引入 在高一学函数性质时,我们会遇到一些抽象函数的问题,先看两道例题: 【例1】已知函数\(f\left(2x+1\right)\)的定义域为\(\left[1,2\right]\),则函数\(f\left(4x+1\right)\)的定义域是 . 【例2】已知函数\(f\left(x\right)\)的定义域为\(\mathrm{R}\)…

P7394 「TOCO Round 1」History

操作树加二分,目前题解区没有这种做法。 发现操作一可逆,可以用操作树,操作三解决。 操作一单点修改没什么好说的。 接下来看操作二。令 \(fa_{x,k}\) 为 \(x\) 的 \(k\) 级祖先。 发现对于每个询问中,如果 \(y\) 为奇数那么答案为 \(0\)。如果 \(y\) 为偶数,那么答案就是…

php网站忘记后台密码忘记怎么办

如果你忘记了PHP网站后台的登录密码,可以通过以下几种方法来尝试恢复或重置密码:检查邮箱:如果在创建账户时设置了找回密码的功能,并且绑定了邮箱,可以先检查注册时使用的邮箱是否有找回密码的邮件。数据库直接修改:通过phpMyAdmin或其他数据库管理工具登录到MySQL数据库…

公司网站新闻图片修改方式

要修改公司网站上的新闻图片,通常可以按照以下步骤操作:备份原图:在进行任何修改之前,确保先备份原始图片文件,以防修改后不满意或出现其他问题。选择工具:根据需要修改的内容选择合适的图像编辑工具。常见的工具有Photoshop、GIMP(免费开源软件)或者在线编辑器如Canva…

网站提示数据库连接错误

当遇到网站提示数据库连接错误时,可以按照以下步骤进行排查和解决:检查数据库服务器状态:确认数据库服务是否正常运行。 使用命令行工具或管理界面尝试连接数据库。检查配置信息:核对应用中的数据库连接配置(如用户名、密码、主机地址、端口号)是否正确。 确保配置文件没…

宝塔面板忘记管理员用户名密码简单有效解决方法

宝塔面板是一款流行的服务器管理工具,如果忘记了管理员的用户名或密码,可以通过以下步骤来尝试恢复:登录宝塔官网获取帮助访问宝塔官网或者官方论坛,查找相关问题的解决方案。通过命令行重置密码使用SSH工具连接到服务器。 输入以下命令重置密码:bashpt-login按照提示操作…

网站修改后台登录密码

要修改网站后台的登录密码,通常可以通过以下几个步骤来实现:登录后台: 使用当前的用户名和密码登录到网站的管理后台。 进入用户设置: 在后台管理界面找到用户设置或个人信息设置的相关选项。 密码修改页面: 在用户设置中找到修改密码的选项或链接。 填写新密码: 按照提示输入…

河道水位自动监测预警系统

河道水位自动监测预警系统基于AI视频智能水尺读数技术,河道水位自动监测预警系统通过在河道周边布设监控摄像头,实时监测水位的变化。河道水位自动监测预警系统利用人工智能视觉分析技术,进行水位趋势分析和预测。一旦水位超过预设阈值,河道水位自动监测预警系统将自动发出…

帝国cms网站安装时找不到“增加信息”的地方怎么办?

安装时找不到“增加信息”的地方原因:未增加栏目。 解决:先增加栏目,然后再增加信息。扫码添加技术【解决问题】专注中小企业网站建设、网站安全12年。熟悉各种CMS,精通PHP+MYSQL、HTML5、CSS3、Javascript等。承接:企业仿站、网站修改、网站改版、BUG修复、问题处理、二次…

CSP 集训(9.23-9.26)

更新了 CSP-3 网络流那道题解法 updated on 9.26用来整理模拟赛等9.23 csp-3【noip23 ZR二十连测 DAY10】 保龄. A.奇观 狗市题目描述。不是这题意太大歧义了吧,我讨厌的第二种出题人——题意描述相当不清。CTH:13 座城市又不代表是 13 座不同的城市。直接看形式化题目的话(…

SVN提交约束(钩子脚本)Windows环境

将check.py 和 pre-commit.bat文件放到 svn 路径下的hooks文件夹下,其中check.py为格式 pre-commit.bat为windows环境下 执行脚本文件 校验正则格式为: r^项目:.+\n修改内容:.+\n修改时间:\d{4}-\d{2}-\d{2}\n修改人:.+$ check.py内容:import re import sysmessage = s…

网页设计模板怎么使用-如何去修改呢?

使用和修改网页设计模板通常涉及以下几个步骤:选择合适的模板:根据你的网站需求选择一个适合的设计模板。 下载或安装模板:从模板提供商处下载模板文件,并按照说明将其安装到你的网站系统中。 了解模板结构:通过查看模板的HTML、CSS和JavaScript文件来理解其基本结构和样式…