难度:提高组
考场得分:100+85+50+25
[数学] T1 GCD
定义 f ( x ) = g c d ( x 除1以外所有因数 ) f(x)=gcd(x\text{除1以外所有因数}) f(x)=gcd(x除1以外所有因数) ,求 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,b≤107 。
打表题,数学的直觉提示将数分成3类:
- x x x 为质数时 f ( x ) = x f(x)=x f(x)=x 。
- x x x 可以写成 p k p^k pk ,其中 p p p 为质数, f ( x ) = p f(x)=p f(x)=p 。
- 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,m≤105,x≤106 。
又是一道数学题,不难发现可以预处理出每个数能否被包含,具体可以倒叙枚举并用比自己大的数枚举自己, 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 ∣s∣≤104,∣t∣≤105,n≤105。
本来以为是道字符串题…首先定义 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 n≤105 。
这是一道很神奇的压轴题(其实并没有什么高深的算法)。把不让呆在某一个转换成可以呆在某一格,用 ( 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;
}