2022牛客寒假算法基础集训营4
A-R
长度为\(n\)的字符串,只包含大写字母,问有多少段连续子串能满足包含至少\(k\)个“\(R\)”且不包含“\(P\)”。\((1\leq n\leq200000,1\leq k\leq20)\)
思路
由于不要“\(P\)”,所以就很容易想到在每一段没有“\(P\)”的串中尺取,然后就结束了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[1000005];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
scanf("%s",s+1);
int pre=1;
ll ans=0;
for(int i=1;i<=n+1;i++)
{
if(i==n+1||s[i]=='P')
{
int l=pre,r=pre;
int num=(s[l]=='R');
while(r<i)
{
if(num<m)
{
r++;
num+=(s[r]=='R');
}
if(num>=m)
{
ans+=(i-r);
num-=(s[l]=='R');
l++;
}
}
pre=i+1;
}
}
printf("%lld\n",ans);
return 0;
}
B-进制
长度为\(n\)的字符串,\(q\)组操作,每次输入\(3\)个整数\(op,x,y\)。若\(op\)为\(1\),则将第\(x\)个数字改为\(y\);若\(op\)为\(2\),查询区间\([x,y]\)所能表示某进制的最小值(进制必须合法且在二进制到十进制之间,可包含前导0),对\(1e9+7\)取模。\((1\leq n,q\leq10^5)\)
思路
敲十棵线段树应该就能解决问题了吧(逃
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
char s[100005];
ll k[100005];
ll tree[11][400005];
ll poww(ll x,ll n)
{
ll ans=1;
while(n)
{
if(n&1)(ans*=x)%=mod;
(x*=x)%=mod;
n>>=1;
}
return ans;
}
void pushup(int p,int l,int r)
{
int mid=(l+r)>>1;
tree[1][p]=max(tree[1][p<<1],tree[1][p<<1|1]);
for(int i=2;i<=10;i++)
{
tree[i][p]=(tree[i][p<<1]*poww(i,r-mid)%mod+tree[i][p<<1|1])%mod;
}
}
void build(int p,int l,int r)
{
if(l==r)
{
for(int i=1;i<=10;i++)tree[i][p]=k[l];
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p,l,r);
}
void update(int p,int l,int r,int x,int y)
{
if(l==r)
{
for(int i=1;i<=10;i++)tree[i][p]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid)update(p<<1,l,mid,x,y);
else update(p<<1|1,mid+1,r,x,y);
pushup(p,l,r);
}
ll query(int p,int l,int r,int x,int y,int op)
{
if(x<=l&&r<=y)
{
if(op==1)return tree[op][p];
else return tree[op][p]*poww(op,y-r)%mod;
}
int mid=(l+r)>>1;
if(op==1)
{
ll ans=0;
if(x<=mid)ans=max(ans,query(p<<1,l,mid,x,y,op));
if(mid<y)ans=max(ans,query(p<<1|1,mid+1,r,x,y,op));
return ans;
}
else
{
ll ans=0;
if(x<=mid)(ans+=query(p<<1,l,mid,x,y,op))%=mod;
if(mid<y)(ans+=query(p<<1|1,mid+1,r,x,y,op))%=mod;
return ans;
}
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(int i=1;i<=n;i++)k[i]=s[i]-'0';
build(1,1,n);
while(q--)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(op==1)
{
update(1,1,n,x,y);
}
else
{
ll maxx=query(1,1,n,x,y,1);
printf("%lld\n",query(1,1,n,x,y,maxx+1));
}
}
return 0;
}
C-蓝彗星
有\(n\)颗星,每颗星在天中保留\(t\)秒,\(s_i\)代表第\(i\)颗星的颜色,“\(B\)”代表蓝色,“\(R\)”代表红色,\(a_i\)代表的\(i\)颗星出现的时间,假设黑夜无限长,问能看到蓝色彗星且看不到红色彗星的时长。\((1\leq n,t,a_i\leq100000)\)
思路
对于这样的题目应该一下就能想到差分,一想到差分应该就直接结束了吧……
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mp[1000005];
char s[1000005];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
int a;
scanf("%d",&a);
if(s[i]=='B')
{
mp[a]++;
mp[a+m]--;
}
else
{
mp[a]-=1000000;
mp[a+m]+=1000000;
}
}
ll num=0;
int tot=0;
for(int i=0;i<=1000000;i++)
{
num+=mp[i];
if(num>0)tot++;
}
printf("%d\n",tot);
return 0;
}
D-雪色光晕
小红初始位置为\((x_0,y_0)\),她每秒有一个方向向量\((x_i,y_i)\),会沿着该方向跑一秒,小红想知道他在跑步过程中距离小果\((x,y)\)的最近距离。\((1\leq n\leq200000,-10^9\leq x,y,x_i,y_i\leq10^9)\)
思路
这个问题就是判断线段外一点到线段的最短距离是等于到直线的距离还是到端点的距离,然后稍微运用一下高中知识就能轻松\(AC\)了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int _;
scanf("%d",&_);
double x0,y0,x,y;
scanf("%lf%lf%lf%lf",&x0,&y0,&x,&y);
double minn=1e9;
while(_--)
{
double x1,y1;
scanf("%lf%lf",&x1,&y1);
double ans=fabs(y1*x-x1*y+x1*y0-y1*x0)/sqrt(x1*x1+y1*y1);
if(x1*(x-x0)+y1*(y-y0)>=0&&(x1*x1+y1*y1)>=((x-x0)*(x-x0)+(y-y0)*(y-y0)-ans*ans))minn=min(minn,ans);
else minn=min(minn,min(sqrt((x-x0)*(x-x0)+(y-y0)*(y-y0)),sqrt((x-x0-x1)*(x-x0-x1)+(y-y0-y1)*(y-y0-y1))));
x0+=x1,y0+=y1;
}
printf("%.8f\n",minn);
return 0;
}
E-真假签到题
小红拿到了一份代码:
long long f(long long x) { if(x==1)return 1; return f(x/2)+f(x/2+x%2); }
对于给定的\(x\),问\(f(x)\)是多少。\((1\leq x\leq10^{18})\)
代码
第一道签到题,但是一开始有点慌,规律先错了,哭死。所以其实就是一道输入输出题。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll n;
scanf("%lld",&n);
printf("%lld\n",n);
return 0;
}
F-小红的记谱法
\((字符串长度不超过1000)\)
思路
这题应该就是一道很简单的模拟题吧……
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[1000005];
int main()
{
scanf("%s",s+1);
int num=0;
int len=strlen(s+1);
for(int i=1;i<=len;i++)
{
if(s[i]=='>')num++;
else if(s[i]=='<')num--;
else
{
int n=s[i]-'A'+1;
if((n+7-2)%7==0)printf("7");
else printf("%d",(n+7-2)%7);
for(int i=1;i<=num;i++)printf("*");
for(int i=num;i<=-1;i++)printf(".");
}
}
return 0;
}
G-子序列权值乘积
规定一个数组的权值为最大值乘上最小值。小红有一个数组,问这个数组的所有非空子序列的权值之积对\(1e9+7\)取模后的答案是多少。\((1\leq n\leq200000,1\leq a_i\leq10^9)\)
思路
首先应该就能想到对这个序列排个序,然后对于一个数,他前面有\(a\)个数,后面有\(b\)个数,那么他就有\(2\)的\(a\)次方的机会可以成为最大值,最小值也同理,然后差不多就能结束了,不过还要知道一个芝士点叫做欧拉降幂:
ll phi(ll n) { ll rec=n; for(int i=2;i*i<=n;i++) { if(n%i==0) { rec=rec-rec/i; while(n%i==0) n/=i; } } if(n>1) rec=rec-rec/n; return rec; } //一般情况下poww(a,b)%c当b很大时,就能写成 //poww(a,b%phi(mod)+phi(mod))%mod //而对于gcd(a,mod)==1的情况,phi(mod)就变成了mod-1 //于是得到poww(a,b)%mod==poww(a,b%(mod-1))%mod //注:对于非特殊情况,"+phi(mod)"不能省
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll a[1000005];
ll base[1000005];
ll poww(ll x,ll n)
{
ll ans=1;
while(n)
{
if(n&1)(ans*=x)%=mod;
(x*=x)%=mod;
n>>=1;
}
return ans;
}
int main()
{
int n;
scanf("%d",&n);
base[0]=1;
for(int i=1;i<=n;i++)base[i]=base[i-1]*2%(mod-1);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
sort(a+1,a+n+1);
ll ans=1;
for(int i=1;i<=n;i++)
{
(ans*=poww(a[i],base[i-1]+base[n-i]))%=mod;
}
printf("%lld\n",ans);
}
H-真真真真真签到题
小红和小紫在一个正方体中,小紫在正中间,小红在角落,两人相距\(x\),问正方体的体积。\((1\leq x\leq100)\)
思路
第二道签到题,没什么难度吧应该……
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
double dis;
scanf("%lf",&dis);
double a=2*sqrt(dis*dis/3);
printf("%.6f\n",a*a*a);
return 0;
}
I-爆炸的符卡洋洋洒洒
有\(n\)张符卡,每张符卡的魔力消耗为\(a_i\),威力为\(b_i\)。小红要保证发出的符卡的魔力消耗之和为\(k\)的倍数,问能够发出的最大威力是多少。\((1\leq n,k\leq1000,1\leq a_i,b_i\leq10^9)\)
思路
首先想到的当然是背包,但是\(1e9\)当然是存不下的,但是由于\(k\)的值不大,所以直接存模\(k\)后的值就能轻松解决啦。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[1005][1005];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
ll a,b;
scanf("%lld%lld",&a,&b);
for(int j=0;j<k;j++)
{
dp[i][a%k]=max(dp[i][a%k],b);
if(dp[i-1][j])
{
dp[i][j]=max(dp[i-1][j],dp[i][j]);
dp[i][(j+a)%k]=max(dp[i-1][j]+b,dp[i][(j+a)%k]);
}
}
}
if(dp[n][0]==0)printf("-1\n");
else printf("%lld\n",dp[n][0]);
}
J-区间合数的最小公倍数
小红有两个整数\(l,r\),问\([l,r]\)中所有合数的最小公倍数是多少,答案对\(1e9+7\)取模。\((1\leq l\leq r\leq30000)\)
思路
这道题我一开始理a假了,其实就是小学的时候求最小公倍数的方法……
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
bool prime[30005];
ll t[30005];
ll poww(ll a,ll n)
{
ll ans=1;
while(n)
{
if(n&1)ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
int main()
{
for(ll i=2;i<=30000;i++)
for(ll j=2;j*i<=30000;j++)
prime[i*j]=1;
ll l,r;
scanf("%lld%lld",&l,&r);
ll num=0;
for(ll i=l;i<=r;i++)
{
if(prime[i])
{
num++;
ll n=i;
for(ll j=2;j<=n;j++)
{
if(!prime[j])
{
ll numm=0;
while(n&&n%j==0)numm++,n/=j;
t[j]=max(t[j],numm);
}
}
}
}
if(num==0)printf("-1\n");
else
{
ll ans=1;
for(int i=2;i<=30000;i++)if(t[i])
{
ans=ans%mod*poww(i,t[i])%mod;
}
printf("%lld\n",ans);
}
return 0;
}
K-小红的真真假假签到题题
小红拿到了一个正整数\(x\),她想构造一个正整数\(y\)使得:\(1、\)\(y\)是\(x\)的倍数且\(y\)不等于\(x\);\(2、\)\(x\)在二进制表示下是\(y\)在二进制表示下的一个字串且在二进制表示下\(1\)的个数不同;\(3、\)\(y\)为不超过\(10^{19}\)的正整数。\((1\leq x\leq10^9)\)
思路
我们可以拿\(5\)举例,\(5\)的二进制表示为\(101\),而\(101101\)能作为\(5\)的\(y\),也就是\(101*1001=5*9=45\),因此我们得出,\(y=x*(1+(1<<len(x)))\),其中\(len(x)\)代表\(x\)的二进制表示的长度。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll x;
scanf("%lld",&x);
int len;
for(int i=30;i>=0;i--)if(x&(1ll<<i)){len=i+1;break;}
ll mul=1+(1ll<<len);
printf("%lld\n",x*mul);
return 0;
}
L-在这冷漠的世界里光光哭哭
有一个字符串\(S\),\(q\)组询问,每次问在区间\([l,r]\)中有多少个长度为\(3\)的字符串\(s\)的子序列。\((1\leq n\leq80000,1\leq q\leq500000)\)
由于还不是很会,就只能参考solemntee大佬的题解了