[模拟赛]2022.07.11

难度:提高组

考场得分:100+85+50+25


[数学] T1 GCD

定义 f ( x ) = g c d ( x 除1以外所有因数 ) f(x)=gcd(x\text{除1以外所有因数}) f(x)=gcd(x1以外所有因数) ,求 f ( a ) + f ( a + 1 ) + . . . + f ( b ) f(a)+f(a+1)+...+f(b) f(a)+f(a+1)+...+f(b) a , b ≤ 1 0 7 a,b\leq 10^7 a,b107


打表题,数学的直觉提示将数分成3类:

  1. x x x 为质数时 f ( x ) = x f(x)=x f(x)=x
  2. x x x 可以写成 p k p^k pk ,其中 p p p 为质数, f ( x ) = p f(x)=p f(x)=p
  3. x x x 有两个或以上的质因数, f ( x ) = 1 f(x)=1 f(x)=1

由此有了处理思路:先扫一遍质数,若是质数,则将所有小于 b b b 的倍数扫出来,使得 f ( i × b ) = 1    o r    b f(i\times b)=1\,\,or\,\,b f(i×b)=1orb ,其中若 f ( i ) ≠ i f(i)\neq i f(i)=i 则为1(之前扫到过),否则为 i i i 。然而这样是 O ( n   l o g   l o g   n ) O(n\,log\,log\,n) O(nloglogn) ,勉强卡过。正解使用的是线性筛法,先处理好之后在一次性跳质数的 k k k 次幂。

#include<bits/stdc++.h>
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;++i)
#define ROF(i,j,k) for(int i=j;i>=k;--i)
#define mp make_pair
#define pb push_back
#define pii pair< int , int >
#define rg register
namespace IO{
	inline ll in(){
		ll x=0,f=1;
		char c;
		do{
			c=getchar();
			if(c=='-')
				f=-1;
		}while(c<'0' || c>'9');
		while(c<='9' && c>='0'){
			x=(x<<3)+(x<<1)+c-'0';
			c=getchar();
		}
		return f*x;
	}
}

int gcd(int x,int y){
	return (y==0)?x:gcd(y,x%y);
}


using namespace IO;
int a,b,f[10000005],g[10000005];
ll ans;
int main(){
	a=in(),b=in();
	FOR(i,2,b){
		f[i]=i;
	}
	FOR(i,2,b){
		if(g[i]==0){
			for(int j=i*2;j<=b;j+=i){
				if(f[j]!=j)
					f[j]=1;
				else
					f[j]=i;
				g[j]=1;
			}
		}
	}
	FOR(i,a,b){
		ans+=f[i];
	}
	printf("%lld\n",ans);
	return 0;
}

[数学] T2 包含

给出集合 Q Q Q n n n 个数,询问 m m m 次是否有 Q Q Q 中数包含 x x x (询问给出)。 a a a 包含 b b b 表示 a & b = b a\&b=b a&b=b n , m ≤ 1 0 5 , x ≤ 1 0 6 n,m\leq 10^5,x\leq 10^6 n,m105,x106


又是一道数学题,不难发现可以预处理出每个数能否被包含,具体可以倒叙枚举并用比自己大的数枚举自己, f [ i ] = f [ i ] ∣ f [ i & 2 j ] f[i]=f[i]|f[i\& 2^j] f[i]=f[i]f[i&2j] 。这样的做法是 O ( 1 0 6   l o g   ( 1 0 6 ) ) O(10^6\,log\,(10^6)) O(106log(106)) 的,完全可以过。

#include<bits/stdc++.h>
#define ll long long
#define rg register
#define FOR(i,j,k) for(rg int i=j;i<=k;++i)
#define ROF(i,j,k) for(rg int i=j;i>=k;--i)
#define mp make_pair
#define pb push_back
#define pii pair< int , int >
namespace IO{
	inline ll in(){
		ll x=0,f=1;
		char c;
		do{
			c=getchar();
			if(c=='-')
				f=-1;
		}while(c<'0' || c>'9');
		while(c<='9' && c>='0'){
			x=(x<<3)+(x<<1)+c-'0';
			c=getchar();
;		}
		return f*x;
	}
}
using namespace IO;
int n,m,tcnt=1;
int a[100005];
int f[1000005];

int main(){
	n=in(),m=in();
	FOR(i,1,n){
		a[i]=in();
		f[a[i]]=1;
	}
	ROF(i,1000000,1){
		for(int j=1;j+i<=1000000;j=j*2){
			if(i&j)
				continue;
			f[i]|=f[i+j];
		}
	}
	while(m--){
		int q=in();
		if(f[q]){
			printf("yes\n");
		}
		else{
			printf("no\n");
		}
	}
	return 0;
}

[大模拟] T3 前缀

给定串 s s s t t t ,其中 s s s 只含小写字母, t t t 中有 ∗ * 号,可以用任何字符代替。同时 t t t 中含有数字,例如 a 3 a3 a3 等价于 a a a aaa aaa ∗ 10000 *10000 10000 相当于10000个星号相连。求 s s s s s s . . . ssssss... ssssss... 匹配上 t t t 的最小长度。(含多个 t t t 串,答案对998244353取模) ∣ s ∣ ≤ 1 0 4 , ∣ t ∣ ≤ 1 0 5 , n ≤ 1 0 5 |s|\leq 10^4,|t|\leq 10^5,n\leq 10^5 s104,t105,n105


本来以为是道字符串题…首先定义 n x t [ i ] [ j ] nxt[i][j] nxt[i][j] 表示在 s s s 串的 i i i 位置上最近的下一个字母 j j j ,这个过程可以在 26 × ∣ s ∣ 26\times |s| 26×s 的时间内通过倒序枚举得到,然后每一次操作分为带数字和不带数字两种。对于不带数字的情况,直接找到 n x t [ i ] [ j ] nxt[i][j] nxt[i][j] 即可,若 n x t [ i ] [ j ] = − 1 nxt[i][j]=-1 nxt[i][j]=1 就重新从开头寻找。对于带数字的情况,需要通过一边读入一边取模的方法快速得出答案, ∗ * 直接扫描之后向后走一格。

#include<bits/stdc++.h>
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;++i)
#define ROF(i,j,k) for(int i=j;i>=k;--i)
#define mp make_pair
#define pb push_back
#define pii pair< int , int >
#define rg register
#define P 998244353
namespace IO{
	inline ll in(){
		ll x=0,f=1;
		char c;
		do{
			c=getchar();
			if(c=='-')
				f=-1;
		}while(c<'0' || c>'9');
		while(c<='9' && c>='0'){
			x=(x<<3)+(x<<1)+c-'0';
			c=getchar();
		}
		return f*x;
	}
}
using namespace IO;
char s[100005],t[100005];
int nxt[100005][26];
int cnt[26];
ll n,slen,tlen,p,flag;
char rest;
ll ans;
int main(){
	scanf("%s",s);
	slen=strlen(s);
	FOR(i,0,25){
		nxt[slen][i]=-1;
	}
	ROF(i,slen-1,0){
		FOR(j,0,25){
			nxt[i][j]=nxt[i+1][j];
		}
		nxt[i][s[i]-'a']=i;
		cnt[s[i]-'a']++;
	}
	n=in();
	while(n--){
		scanf("%s",t);
		tlen=strlen(t);
		ans=0,p=0,flag=0;
		FOR(i,0,tlen-1){
			if(t[i+1]<='9' && t[i+1]>='0'){
				rest=t[i];
				if(rest=='*'){
					i++;
					ll x=0,y=0;
					while(t[i]<='9' && t[i]>='0'){
						x=(x<<3)+(x<<1)+t[i]-'0';
						y=(y<<3)+(y<<1)+t[i]-'0';
						x=x%P;
						y=y%slen;
						++i;
					}
					ans=(ans+x)%P;
					p=(p+y)%slen;
					--i;
				}
				else{
					i++;
					ll x=0,y=0;
					while(t[i]<='9' && t[i]>='0'){
						x=(x<<3)+(x<<1)+t[i]-'0';
						y=(y<<3)+(y<<1);
						y=y%P;
						++i;
						if(x/cnt[rest-'a']>1){
							y+=slen*(x/cnt[rest-'a']-1);
							y=y%P;
							x-=(x/cnt[rest-'a']-1)*cnt[rest-'a'];
						}
					}
					ans=(ans+y)%P;
					while(x--){
						if(nxt[p][rest-'a']==-1){
							ans+=slen-p;
							p=0;
						}
						if(p==0 && nxt[p][rest-'a']==-1){
							flag=1;
							break;
						}
						ans=(nxt[p][rest-'a']-p+1+ans)%P;
						p=nxt[p][rest-'a']+1;
						p=p%slen;
					}
					--i;
				}
			}
			else{
				if(t[i]=='*'){
					ans=(ans+1)%P;
					p=(p+1)%slen;
				}
				else{
					if(nxt[p][t[i]-'a']==-1){
						ans+=slen-p;
						p=0;
					}
					if(p==0 && nxt[p][t[i]-'a']==-1){
						flag=1;
						break;
					}
					ans=(nxt[p][t[i]-'a']-p+1+ans)%P;
					p=nxt[p][t[i]-'a']+1;
					p=p%slen;
				}
			}
		}
		printf("%lld\n",(flag)?-1:ans%P);
	}
	return 0;
}

[?]T4 移动

牛牛从0出发走到 n + 1 n+1 n+1 ,每秒可以选择向前走一步,向后走一步或者不走,有一些时刻不让呆在某一格,求最短到达时间, n ≤ 1 0 5 n\leq 10^5 n105


这是一道很神奇的压轴题(其实并没有什么高深的算法)。把不让呆在某一个转换成可以呆在某一格,用 ( x , i , t ) (x,i,t) (x,i,t) 表示一个状态:在第 x x x 的位置走到第 i i i 个可以走到的点的时间 t t t 。并用 f [ i ] f[i] f[i] 表示到第 i i i 段时间的最短时间,然后用优先队列不断更新状态,最后就可以得到答案。

#include<bits/stdc++.h>
#define ll long long
#define rg register
#define FOR(x,y,z) for(rg int x=y;x<=z;++x)
#define ROF(x,y,z) for(rg int x=y;x>=z;--x)
#define mp std::make_pair
#define pb push_back
#define pii std::pair< int , int >
#define Inf 0x3f3f3f3f
namespace IO{
	inline ll in(){
		ll x=0,f=1;
		char c;
		do{
			c=getchar();
			if(c=='-')
				f=-1;
		}while(c<'0' || c>'9');
		while(c<='9' && c>='0'){
			x=(x<<3)+(x<<1)+c-'0';
			c=getchar();
		}
		return f*x;
	}
}
using namespace IO;

std::vector< pii > v[200005],cur;
struct node{
	int id,x,t;
	node(int a,int b,int c){
		id=a;
		x=b;
		t=c;
	}
};

int n,m;
int id[200005],f[200005];
bool operator > (node a,node b){
	return a.t>b.t;
}

std::priority_queue< node , std::vector<node> , std::greater<node> > q;

void calc(node p,int x){
	int r=v[p.x][p.id-id[p.x]].second;
	int i=std::lower_bound(v[x].begin(),v[x].end(),mp(p.t+1,0))-v[x].begin()-1;
	if(v[x][i].second>=p.t+1){
		if(f[id[x]+i]>p.t+1){
			f[id[x]+i]=p.t+1;
			q.push(node(id[x]+i,x,p.t+1));
		}
	}
	++i;
	while(i<(int)v[x].size() && v[x][i].first<=r+1){
		if(f[id[x]+i]>v[x][i].first){
			f[id[x]+i]=v[x][i].first;
			q.push(node(id[x]+i,x,v[x][i].first));
		}
		++i;
	}
}

void solve(){
	memset(f,0x3f,sizeof(f));
	f[0]=0;
	q.push(node(0,0,0));
	while(q.size()){
		node p=q.top();
		q.pop();
		if(p.t>f[p.id])
			continue;
		if(p.x>0)
			calc(p,p.x-1);
		if(p.x<n+1)
			calc(p,p.x+1);
	}
}

int main(){
	n=in(),m=in();
	FOR(i,1,m){
		int a=in(),b=in(),c=in();
		v[a].pb(mp(b,c));
	}
	v[0].pb(mp(0,Inf));
	v[n+1].pb(mp(0,Inf));
	id[1]=1;
	FOR(i,1,n){
		std::sort(v[i].begin(),v[i].end());
		cur.clear();
		int r=-1;
		FOR(j,0,(int)v[i].size()-1){
			pii p=v[i][j];
			if(p.first>r+1)
				cur.pb(mp(r+1,p.first-1));
			r=std::max(r,p.second);
		}
		cur.pb(mp(r+1,Inf));
		v[i]=cur;
		id[i+1]=id[i]+v[i].size();
	}
	solve();
	printf("%d\n",f[id[n+1]]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值