2022.7.11 模拟赛
2022 7.11 模拟赛
比赛 link
\(GCD\)
题意:
定义 \(f(x)=\gcd(\text{除1以外所有因子})\)
给定 \(a,b\),求 \(\sum_{i=a}^b f(i)\)
\(1<a< b\le 10^7\)
思路:
签到题
容易发现,当 \(x\) 为合数且 \(x\) 有两个不同质因子时,\(f(x)=1\)
其余时候 \(f(x)=a\),\(a\in prime\) 且 \(a|x\)
线性筛质数并更新就行了
时间复杂度 \(O(a+b)\)
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+1;
#define ll long long
#define int long long
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int cnt;
int p[7000000];
bool vis[N];
int can[N];
inline void prework(){
can[1]=1;
for(int i=2;i<N;++i){
can[i]=1;
if(!vis[i])
p[++cnt]=i,can[i]=i;
for(int j=1;j<=cnt&&i*p[j]<N;++j){
vis[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
for(int i=1;i<=cnt;++i)
for(ll j=p[i];j<N;j*=p[i]){
can[j]=p[i];
}
}
inline int solve(int n){
ll res=0;
for(int i=1;i<=n;++i)
res+=1ll*can[i];
return res;
}
signed main(){
prework();
int a=read(),b=read();
printf("%lld\n",solve(b)-solve(a-1));
}
注意 \(i\cdot p[j]\) 的时候会爆 \(int\)
不开 \(long\) \(long\) 的下场:\(100pts\rightarrow 20pts\) \({\color{grey}wdnmd}\)
\({\huge一定要开 long long 啊!}\)
包含
题意:
定义 \(A\) 包含 \(B\):\(A\&B=B\)
给定一个有 \(n\) 个正整数 \(a_{1\sim n}\) 的集合 \(S\) 和 \(m\) 次询问
每次询问给你一个数 \(x\),请回答 \(Q\) 中是否存在一个数包含 \(x\)
\(1\le n,m\le 10^5,1\le x\le a_i\le10^6\)
思路:
第一想法:爆搜
暴力枚举并更新 \(a_{1\sim n}\) 可以包含的数,这样可以做到每次查询 \(O(1)\)
时间复杂度 \(O(2^{20}\cdot n)\)
很显然不够优,考虑记忆化
若是一个数 \(x\in a_i\) 且 \(x\) 所有包含的数已经更新过了,那么当搜 \(a_i\) 搜到 \(x\) 时就可以不用更新了
这样每个数最多只会被更新一次,保证了复杂度
时间复杂度 \(O(n\log a_i+m)\)
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+1;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int n,m,f[N];
inline void dfs(int x,int now,int bit){
if(f[now]) return;
if(bit>=20){
f[now]=1;
return;
}
if((x>>bit)&1) dfs(x,now-(1<<bit),bit+1);
dfs(x,now,bit+1);
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;++i){
int x=read();
dfs(x,x,0);
}
for(int i=1;i<=m;++i){
int x=read();
puts(f[x]==1?"yes":"no");
}
}
考场上第一想法是 \(trie\),但最后写的还是爆搜
但为什么比别人的爆搜跑的都慢啊 \(QAQ\)
最后只有 \(50pts\)
前缀
题意:
牛牛有一个 \(𝑠\) 串,\(𝑠\)串仅由 \(26\) 个小写英文字母组成,他现在将 \(𝑠\) 串进行了无限次的复制扩展成了一个无线循环串。
例如一开始 \(𝑠 =\) "\(𝑎𝑏𝑐\)",那么牛牛就会将其变为"\(𝑎𝑏𝑐𝑎𝑏𝑐𝑎𝑏𝑐\cdots\) "
若某个字符串保留其原本字符出现的顺序,并且按照顺序取出若干个字符。可以不连续,可以不取。
我们称取出的这若干个字符连成的字符串为一个子序列。
若连续取出某个字符串的前 \(𝑘\) 个字符,组成一个子串,我们称该字符串为原串长度为 \(𝑘\) 的前缀。
对于一个字符串\(𝑡\),若某字符串的至少一个子序列为 \(𝑡\) 。则称它是一个“含 \(𝑡\) 序列串”
牛牛想要知道对于给定的 \(𝑡\),他想要知道 \(𝑠\) 的一个最短前缀满足它是一个“含 \(𝑡\) 序列串”,它的长度有多长?
由于答案可能非常大,所以他要求你输出答案对 \(998244353\) 取余数后的结果即可。
特别的,如果 \(𝑆\) 串不存在任何一个前缀满足他是一个“含 \(𝑡\) 序列串”,请输出" \(-1\)"表示无解。
\(𝑡\) 串中除了 \(26\) 个英文字母以外还会出现"\(*\)",表示一个通配符。统配符可以视为任意字母。
例如循环 \(𝑠\) 串为"$𝑎𝑏𝑐𝑎𝑏𝑐𝑎𝑏𝑐𝑎𝑏𝑐. . . \(",𝑡串为"\)𝑎 ∗ 𝑐𝑎$$"时,最短含𝑡序列前缀长 \(4\)。而当𝑡串为"\(𝑎 ∗∗ 𝑐𝑎\)"时,最短含 \(𝑡\) 序列前缀长 \(7\)。
除此之外,牛牛输入的𝑡串还可能非常非常长,最长可以达到 \(10^{10^5}\)这么长。
思路:
究极大模拟
还要高精
是真滴毒瘤啊 \(qwq\)
code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int N=1e5+5;
char S[N],t[N];
vector <int> K[27];
int T,id[N][2],c[N],len,ans;
inline void add(int &x,int y){
x+=y;
if(x>=mod) x-=mod;
}
inline int work(int l,int r,int C1,int C2){
int number=0,s=0,tot=0,pos;
for(int i=l;i<=r;++i)
c[i-l+1]=t[i]-'0';
for(pos=r-l+1;!c[pos];--pos);
--c[pos];
for(int i=pos+1;i<=r-l+1;++i)
c[i]=9;
for(int i=l;i<=r;++i,number=s%C1)
s=number*10+c[i-l+1],c[++tot]=s/C1;
s=0;
for(int i=tot;i;--i){
s+=c[i]*C2;
c[i]=s%10;
s/=10;
}
for(int i=1;i<=tot;++i)
s=(s*10+c[i])%mod;
add(ans,s);
return number;
}
signed main(){
scanf("%s",S);
len=strlen(S);
for(int i=0;i<len;++i)
K[S[i]^96].push_back(i),id[i][0]=K[S[i]^96].size()-1;
for(int i=0;i<len;++i)
K[0].push_back(i),id[i][1]=i;
cin>>T;
for(;T;--T){
scanf("%s",t);
int lent=strlen(t);
ans=0;
int l=0,r=0,beg=len-1,flag=1;
for(int f=0,now;l<lent;l=++r){
if(t[l]=='*') t[l]=96,f=1;
else f=0;
int G=t[l]^96,sz=K[G].size(),nxt;
if(!sz){
flag=0;
break;
}
if(l+1<lent&&isdigit(t[l+1]))
while(r+1<lent&&isdigit(t[r+1]))
++r;
if(K[G][sz-1]<=beg)
nxt=K[G][0];
else
nxt=*upper_bound(K[G].begin(),K[G].end(),beg);
if(beg<nxt) add(ans,nxt-beg);
else add(ans,len-beg+nxt);
beg=nxt;
if(l<r){
if(now=work(l+1,r,sz,len)){
nxt=K[G][(id[beg][f]+now)%sz];
if(beg<nxt) add(ans,nxt-beg);
else add(ans,len-beg+nxt);
beg=nxt;
}
}
}
if(!flag) puts("-1");
else printf("%d\n",ans);
}
return 0;
}
考场上没看,直接跳了
你没看到我题面都是直接复制的吗
移动
题意:
共有 \(1\sim n\) 这 \(n\) 个闸门,它们分别处在 \(1\sim n\) 这 \(n\) 个点上
你要从 \(0\) 号点走到 \(n+1\) 号点
告诉你 \(m\) 次闸门 \(x\) 的开关操作,\(x\) 会从 \(l_i\) 开始一直关到 \(r_i\)
若是在某一时刻 \(i\) 号闸门是关着的,那么你不能停在这
每次移到左边或右边的点,且需要花费 \(1\) 单位的时间
求最小需要花费多少时间
\(1\le n,m\le 10^5\)
思路:
考场上乱搞了一个贪心,跑过了所有手造数据,因此直接交了
\(70pts\),code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
#define pb push_back
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int n,m,cnt;
int q[N];
bool vis[N];
struct node{
int s,e;
int id;
inline bool operator < (const node x) const{
return id+s==x.id+x.s?(id==x.id?e>x.e:id>x.id):id+x.s>x.id+s;
}
};
vector <node> p[N];
signed main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
int x=read(),s=read(),e=read();
p[x].pb({s,e,x});
vis[x]=1;
}
for(int i=1;i<=n;++i)
if(!vis[i]) q[++cnt]=i;
q[++cnt]=n+1;
for(int i=1;i<=cnt;++i){
int a=q[i];
for(int j=q[i-1]+1;j<a;++j){
for(auto x:p[j]) p[a].pb(x);
p[j].clear();
}
sort(p[a].begin(),p[a].end());
}
int nowtime=0,nowpos=0;
for(int i=1;i<=cnt;++i){
int a=q[i];
for(auto x:p[a]){
int s=x.s,e=x.e,id=x.id;
if(id-nowpos>=s-nowtime)
if(e>nowtime) nowtime=e,nowpos=id-1;
else if(e==nowtime) nowpos=min(nowpos,id-1);
else{
nowtime+=q[i]-nowpos;
nowpos=q[i];
break;
}
}
if(nowpos!=q[i]){
nowtime+=q[i]-nowpos;
nowpos=q[i];
}
}
cout<<nowtime<<endl;
}
为什么这题分最高啊喂
正解是 \(dp\),但是用最短路维护
因为考虑一个点可以移动到左右两个点,所以整一个 \(priority\_queue\) 记录最短时间并更新左右两个点
更新时需满足两个闸门同时开启
时间复杂度 \(O(n\log m)\)
code:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int inf=2e9;
#define pb push_back
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
struct pii{
int a,b;
inline bool operator < (const pii A) const{
return a==A.a?b<A.b:a<A.a;
}
};
vector <pii> v[N],cur;
int id[N],f[N];
int n,m;
struct pt{
int id,x,t;
};
inline bool operator > (pt a,pt b){
return a.t>b.t;
}
priority_queue <pt,vector <pt>,greater <pt> > p;
inline void cal(pt a,int x){
int r=v[a.x][a.id-id[a.x]].b;
int i=lower_bound(v[x].begin(),v[x].end(),(pii){a.t+1,0})-v[x].begin()-1;
if(v[x][i].b>=a.t+1&&f[id[x]+i]>a.t+1){
f[id[x]+i]=a.t+1;
p.push({id[x]+i,x,a.t+1});
}
++i;
for(;i<(int)v[x].size()&&v[x][i].a<=r+1;++i){
if(f[id[x]+i]>v[x][i].a){
f[id[x]+i]=v[x][i].a;
p.push({id[x]+i,x,v[x][i].a});
}
}
}
signed main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
int x=read(),y=read(),z=read();
v[x].pb({y,z});
}
v[0].pb({0,inf});
v[n+1].pb({0,inf});
id[1]=1;
for(int i=1;i<=n;++i){
sort(v[i].begin(),v[i].end());
cur.clear();
int mx=-1;
for(auto x:v[i]){
if(x.a>mx+1) cur.pb({mx+1,x.a-1});
mx=max(mx,x.b);
}
cur.pb({mx+1,inf});
v[i]=cur;
id[i+1]=id[i]+v[i].size();
}
memset(f,0x3f,sizeof(f));
f[0]=0;
p.push({0,0,0});
while(!p.empty()){
auto x=p.top();
p.pop();
if(x.t>f[x.id]) continue;
if(x.x>0) cal(x,x.x-1);
if(x.x<=n) cal(x,x.x+1);
}
cout<<f[id[n+1]]<<endl;
}