[2027届]NOIP2024模拟赛#2
比昨天好多了,谢天谢地。
My rank : 4th。
好消息:明天放假。
坏消息:后天开学。
好消息:没暑假作业。
坏消息:拉了一堆课。
赛时
先写了T1暴力,然后一眼觉得正解是二进制位运算,所以先跑了。
T2题目给出了正解,照着打,过了 50pts,然后简单一想,过掉了。
T3写了暴力,先跑。
T4写了暴力,然后开始推性质,发现组合数,但是一直挂。
回过头看T3,发现正解写法,写了将近300行,但是到最后也没调出来,然后比赛就结束了。
总分:30+100+20+20=170。
T1
很容易发现题目上的 \(F\) 函数实际上就是 \(\log_2 (x \& -x)\),然后暴力赋值,有30pts。
对于正解,考虑二分第 \(k\) 大,\(check\) 函数的话考虑 \(F\) 函数的值只有 \(log\) 级别,对于前面的 \(n\),容易发现就是一个等差数列。
枚举 \(F\) 的取值,找到对于每个取值在查询的答案之下的数字的个数,加上等差数列,发现仍然单调,所以算法可行。
找到 \(k\) 以后,再去求每个取值中比他大的数字的和,分成两部分,等差数列直接通项公式, \(F\) 的话直接加。
然后做完了,复杂度 \(\mathcal{O}(T\log^2 V)\)。
T2
本场最简单的题,谢谢老师开恩\gb\gb\gb\gb\gb\gb\gb\gb\gb\gb\gb。
前五十分写脸上了,不说了。
后五十分根据前面的情况考虑,容易发现题目所给的 SB-Tree 有一个左儿子<根<右儿子的性质,然后从根开始跑,比较大小决定当前走向即可。
注意分数比较大小要用乘号,否则会像某位军师一样爆精度。
T3
对于没有问号的情况,直接暴力。
思考正解,以当前想要保留 R
为例,我们把数列按照 R
为边界分成多个区间,判断每个 R
是否保留,DP即可。
赛时写了暴力和性质,但是性质写挂了,并且代码成了一大坨,下面给出赛时代码,慎看。
点击查看代码
#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/trie_policy.hpp>
#include<ext/pb_ds/priority_queue.hpp>
#define int long long
using namespace std;
using namespace __gnu_pbds;
//gp_hash_table<string,int>mp2;
//__gnu_pbds::priority_queue<int,less<int>,pairing_heap_tag> q;
inline int read()
{
int w=1,s=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
return w*s;
}
inline int mul(int a,int b,int mod)
{
int A=a*(b>>30ll)%mod*(1ll<<30)%mod;
int B=a*(b&((1ll<<30)-1))%mod;
return (A+B)%mod;
}
const int mod=998244353;
const int maxn=1e6+10;
bool can(char a,char b)
{
if(a=='R'&&b=='P'||a=='P'&&b=='S'||a=='S'&&b=='R')return 0;
return 1;
}
string s;
int n;
int ar,ap,as;
map<string,bool> mp;
void dfs(string s)
{
bool f=1;
for(int i=1;i<s.size();i++)if(s[i-1]!=s[i]){f=0;break;}
if(f)
{
int m=s.size();
if(s[0]=='R')ar=max(ar,m);
else if(s[0]=='P')ap=max(ap,m);
else if(s[0]=='S')as=max(as,m);
return ;
}
if(mp[s])return ;
mp[s]=1;
for(int i=1;i<s.size();i++)
{
if(s[i]==s[i-1])continue;
string t=s;
if(can(s[i-1],s[i]))s.erase(i,1);
else s.erase(i-1,1);
dfs(s);
s=t;
}
}
int pos[maxn],tot,res=0;
int sumr[maxn],sump[maxn],sums[maxn];
char a[maxn];
struct no
{
int l,r;
bool f;
}x[maxn];int cnt=0;
void Subtask()
{
for(int i=0;i<s.size();i++)a[i+1]=s[i];
n=s.size();
for(int i=1;i<=n;i++)
{
sumr[i]=sumr[i-1]+(a[i]=='R');
sump[i]=sump[i-1]+(a[i]=='P');
sums[i]=sums[i-1]+(a[i]=='S');
}
// R
tot=0;cnt=0;
res=0;
pos[++tot]=0;
for(int i=1;i<=n;i++)
{
if(a[i]=='R')pos[++tot]=i;
}
pos[++tot]=n+1;
for(int i=2;i<=tot;i++)
{
int l=pos[i-1]+1,r=pos[i]-1;
if(l>r)continue;
x[++cnt]={l,r,0};
}
for(int i=1;i<=cnt;i++)
{
int l=x[i].l,r=x[i].r;
if(sums[r]-sums[l-1]>0)
{
x[i].f=1;
continue;
}
int ll=i,rr=i,mi=1e9+7;;
while(ll>0)
{
ll--;
if(x[ll].f)
{
int nl=x[ll].r,nr=x[i].l;
mi=min(mi,nr-nl-1);
}
}
}
int l=1e9+7,r=1e9+7,ma=0,sum=0;
for(int i=1;i<=n;i++)
{
if(sums[i])l=min(l,i);
if(sums[i]==sums[n])r=min(r,i);
}
for(int i=1;i<=n;i++)
{
if(a[i]=='S'||a[i]=='P')break;
res++;
}
for(int i=n;i>=1;i--)
{
if(a[i]=='S'||a[i]=='P')break;
res++;
}
for(int i=l;i<=r;i++)
{
if(a[i]=='S')
{
res+=ma;
sum=ma=0;
}
else if(a[i]=='P')
{
sum=0;
}
else if(a[i]=='R')
{
sum++;
ma=max(ma,sum);
}
}
cout<<res<<endl;
// S
tot=0;cnt=0;
res=0;
pos[++tot]=0;
for(int i=1;i<=n;i++)
{
if(a[i]=='S')pos[++tot]=i;
}
pos[++tot]=n+1;
for(int i=2;i<=tot;i++)
{
int l=pos[i-1]+1,r=pos[i]-1;
if(l>r)continue;
x[++cnt]={l,r,0};
}
for(int i=1;i<=cnt;i++)
{
int l=x[i].l,r=x[i].r;
if(sums[r]-sums[l-1]>0)
{
x[i].f=1;
continue;
}
int ll=i,rr=i,mi=1e9+7;;
while(ll>0)
{
ll--;
if(x[ll].f)
{
int nl=x[ll].r,nr=x[i].l;
mi=min(mi,nr-nl-1);
}
}
}
l=1e9+7,r=1e9+7,ma=0,sum=0;
for(int i=1;i<=n;i++)
{
if(sump[i])l=min(l,i);
if(sump[i]==sump[n])r=min(r,i);
}
for(int i=1;i<=n;i++)
{
if(a[i]=='R'||a[i]=='P')break;
res++;
}
for(int i=n;i>=1;i--)
{
if(a[i]=='R'||a[i]=='P')break;
res++;
}
for(int i=l;i<=r;i++)
{
if(a[i]=='P')
{
res+=ma;
sum=ma=0;
}
else if(a[i]=='R')
{
sum=0;
}
else if(a[i]=='S')
{
sum++;
ma=max(ma,sum);
}
}
cout<<res<<endl;
// P
tot=0;cnt=0;
res=0;
l=1e9+7,r=1e9+7;
ma=0,sum=0;
pos[++tot]=0;
for(int i=1;i<=n;i++)
{
if(a[i]=='P')pos[++tot]=i;
}
pos[++tot]=n+1;
for(int i=2;i<=tot;i++)
{
int l=pos[i-1]+1,r=pos[i]-1;
if(l>r)continue;
x[++cnt]={l,r,0};
}
for(int i=1;i<=cnt;i++)
{
int ll=i,rr=i,mi=1e9+7;;
while(ll>0)
{
ll--;
if(x[ll].f)
{
int nl=x[ll].r,nr=x[i].l;
mi=min(mi,nr-nl-1);
}
}
}
for(int i=1;i<=n;i++)
{
if(sumr[i])l=min(l,i);
if(sumr[i]==sumr[n])r=min(r,i);
}
for(int i=1;i<=n;i++)
{
if(a[i]=='S'||a[i]=='R')break;
res++;
}
for(int i=n;i>=1;i--)
{
if(a[i]=='S'||a[i]=='R')break;
res++;
}
for(int i=l;i<=r;i++)
{
if(a[i]=='R')
{
res+=ma;
sum=ma=0;
}
else if(a[i]=='S')
{
sum=0;
}
else if(a[i]=='P')
{
sum++;
ma=max(ma,sum);
}
}
cout<<res<<endl;
}
signed main()
{
freopen("rps.in","r",stdin);
freopen("rps.out","w",stdout);
cin>>s;
n=s.size();
if(n<=11){dfs(s);printf("%lld\n%lld\n%lld\n",ar,ap,as);return 0;}
bool Sub=1;
for(int i=0;i<n;i++){if(s[i]=='?')Sub=0;break;}
if(Sub){Subtask();return 0;}
return 0;
}
//RPSRPSRPSRS
T4
赛时写了暴力,有20pts。
去研究一下 \(a=0\) 的性质,发现和组合数有关,推了一会发现推不下去了。
对于正解,考虑状压。
裸的有50pts,但是优化的话严格超纲,所以不写了。
总结
啦啦啦。