冲刺国赛模拟 36
感觉思想都不难,但是场上不是想不出来就是写不动,见了鬼了。
染色题
考虑一个转化:对于奇数位置,在 \(a_i\neq a_{i+2}\) 的地方放一个红球,对于偶数位置在同样的地方放个蓝球。这样每个区间的限制变成 \([l,r-2]\) 不能同时有红蓝球,转移前缀和优化一下即可。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
const int mod=998244353;
int n,m,dp[1000010],sum1[1000010],sum2[1000010],a[1000010];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)a[i]=i;
for(int i=1;i<=m;i++){
int l,r;scanf("%d%d",&l,&r);
if(r>=2)a[r-2]=min(a[r-2],l);
}
for(int i=n-1;i>=1;i--)a[i]=min(a[i],a[i+1]);
dp[0]=sum2[0]=1;
for(int i=1;i<=n;i++){
if(i&1){
dp[i]=(sum1[i-1]+sum2[a[i]-1])%mod;
sum1[i]=(sum1[i-1]+dp[i])%mod;
sum2[i]=sum2[i-1];
}
else{
dp[i]=(sum2[i-1]+sum1[a[i]-1])%mod;
sum1[i]=sum1[i-1];
sum2[i]=(sum2[i-1]+dp[i])%mod;
}
}
int ans=0;
for(int i=0;i<=n;i++)ans=(ans+dp[i])%mod;
ans=2ll*ans%mod;
printf("%d\n",ans);
}
石头剪刀布
先考虑标准淘汰赛的过程怎么处理:设 \(f_{i,j}\) 为 \([i,i+2^j]\) 的胜者,转移容易倍增合并。
然后考虑设 \(g_{i,j}\) 为 \(u\in[i,i+2^j-1]\) 时 \([i,i+2^j-1]\) 的胜者,转移可以根据 \(f\) 得到。
现在考虑回答询问。容易发现一个区间可以拆成 \(\log\) 个 \(g\),剩下的都是 \(f\)。可以类似线段树地分治递归处理。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <unordered_map>
using namespace std;
const int mod=998244353,inv3=(mod+1)/3;
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
int n,m,trans[3][3],a[300010];
char s[300010];
struct node{
int a[3];
node(){memset(a,0,sizeof(a));}
node operator+(const node&s)const{
node ans;
for(int i=0;i<=2;i++)ans.a[i]=(a[i]+s.a[i])%mod;
return ans;
}
node operator*(const node&s)const{
node ans;
for(int i=0;i<=2;i++){
for(int j=0;j<=2;j++){
ans.a[i]=(ans.a[i]+1ll*a[i]*s.a[j]%mod*trans[i][j])%mod;
if(i==j)continue;
ans.a[j]=(ans.a[j]+1ll*a[i]*s.a[j]%mod*trans[j][i])%mod;
}
}
return ans;
}
}val[3],f[300010][20],g[300010][20];
node query(int L,int R,int l,int r){
if(l<=L&&R<=r)return g[L][__lg(R-L+2)];
int mid=(L+R)>>1;
node val;
if(l<=mid)val=val+f[mid][__lg(R-mid+1)]*query(L,mid-1,l,r);
if(mid<r)val=val+f[L][__lg(mid-L+1)]*query(mid+1,R,l,r);
return val;
}
int main(){
scanf("%d%d%s",&n,&m,s+1);
for(int i=1;i<=n;i++){
if(s[i]=='R')a[i]=0;
if(s[i]=='P')a[i]=1;
if(s[i]=='S')a[i]=2;
}
trans[0][0]=trans[1][1]=trans[2][2]=1;
trans[0][1]=trans[1][2]=inv3;trans[0][2]=inv3<<1;
trans[1][0]=trans[2][1]=inv3<<1;trans[2][0]=inv3;
val[0].a[0]=val[1].a[1]=val[2].a[2]=1;
for(int i=1;i<=n;i++)f[i][0]=g[i][1]=val[a[i]];
for(int j=1;j<=__lg(n)+1;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=f[i][j-1]*f[i+(1<<j-1)][j-1];
}
}
for(int j=2;j<=__lg(n)+1;j++){
for(int i=1;i+(1<<j)-2<=n;i++){
g[i][j]=g[i][j-1]*f[i+(1<<j-1)-1][j-1]+f[i][j-1]*g[i+(1<<j-1)][j-1];
}
}
while(m--){
int l,r,x,y;scanf("%d%d%d%d",&l,&r,&x,&y);
int ans=query(l,r,x,y).a[0];
if(x-l&1)x++;
ans=1ll*ans*qpow((y-x>>1)+1,mod-2)%mod;
printf("%d\n",ans);
}
return 0;
}
树状数组
首先考虑关键性质:减个 lowbit 之后比 lowbit 低的所有位都变成 \(0\),而高位不会被 lowbit 影响。这意味着我们可以找出使得低位全 \(0\) 在跳一段之后仍然为全 \(0\) 的位置,然后只会跳 \(O(k)\) 次。低位可以预处理每个位置以 \(0\) 开始走到结尾的答案,高位可以直接异或后半段。预处理可以从低位到高位扫,然后每一位从后往前处理,根据之前得到的信息一直跳。
但是这题难度基本全在怎么写。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <unordered_map>
using namespace std;
int n,m,k,A,B;
int nxt[500010][31][2],val[500010],a[500010],ans[500010];
int lowbit(int x){
return x&-x;
}
int query(int l,int r){
return val[r]^val[l-1];
}
void calc(int d){
nxt[n+1][d][0]=n+1;
for(int i=n;i>=1;i--){
if(a[i]==-1)nxt[i][d][0]=nxt[i][d][1]=i+1;
else{
if(nxt[i][d-1][0]){
if((query(i,nxt[i][d-1][0]-1)>>d)&1){
nxt[i][d][0]=nxt[nxt[i][d-1][0]][d][1];
nxt[i][d][1]=nxt[i][d-1][0];
}
else{
nxt[i][d][0]=nxt[i][d-1][0];
nxt[i][d][1]=nxt[nxt[i][d-1][0]][d][1];
}
}
}
}
}
int find(int x){
const int U=(1<<k)-1;
for(int i=k-1;i>=0;i--){
if(nxt[x][i][0]){
int ss=(1<<i+1)-1;ss=U^ss;
return (query(x,n)&ss)|(ans[nxt[x][i][0]]&(U^ss));
}
}
return query(x,n);
}
int lastans;
int getans(int pos,int x){
const int U=(1<<k)-1;
for(int i=0;i<k;i++){
if((x>>i)&1){
if(!nxt[pos][i][1]){
int ss=(1<<i)-1;ss=U^ss;
return ((x^query(pos,n))&ss)|(ans[pos]&(U^ss));
}
int ss=(1<<i+1)-1;ss=U^ss;
x^=query(pos,nxt[pos][i][1]-1)&ss;
x-=lowbit(x);
pos=nxt[pos][i][1];
}
}
return ans[pos];
}
int main(){
scanf("%d%d%d%d%d",&n,&m,&k,&A,&B);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
if(a[i]==-1)val[i]=val[i-1];
else val[i]=val[i-1]^a[i];
}
nxt[n+1][0][0]=n+1;
for(int i=n;i>=1;i--){
if(a[i]==-1)nxt[i][0][0]=nxt[i][0][1]=i+1;
else if(a[i]&1){
nxt[i][0][0]=nxt[i+1][0][1];
nxt[i][0][1]=i+1;
}
else{
nxt[i][0][0]=i+1;
nxt[i][0][1]=nxt[i+1][0][1];
}
}
for(int i=1;i<k;i++)calc(i);
for(int i=n;i>=1;i--)ans[i]=find(i);
while(m--){
int l,x;scanf("%d%d",&l,&x);
l=l^((1ll*A*lastans+B)%n);x=x^((1ll*A*lastans+B)&((1<<k)-1));
printf("%d\n",lastans=getans(l,x));
}
return 0;
}
快踩