Loading

2022.7.11 模拟赛

2022 7.11 模拟赛

\(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;
}
posted @ 2022-07-11 19:44  Into_qwq  阅读(20)  评论(0编辑  收藏  举报