CSP-S模拟32 ( 2025多校CSP模拟赛5 )
CSP-S模拟32 ( 2025多校CSP模拟赛5 )
b
A. 小 Z 爱计数 (count)
签。按 \(a\) 从小到大排序,注意 \(a[i]-a[i-1] <= b[i]\) 且 \(a[i]-a[i-1] >= |b[i]-b[i-1]|\) 时,若 \(a[i]-a[i-1]\) 与 \(|b[i]-b[i-1]|\) 奇偶性不同,答案为 \(\text{No}\) 即可。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar() \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt) \
? EOF \
: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
struct Node{
int a,b;
}a[1<<20];
bool cmp(Node x,Node y) { return x.a<y.a; }
bool pd(int x,int y) { return (x&1)^(y&1)^1; }
void solve()
{
int c=read(),n=read();
for(int i=1;i<=n;i++) a[i].a=read(),a[i].b=read();
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(a[i].a-a[i-1].a>abs(a[i].b)||(a[i].a-a[i-1].a>=abs(a[i].b-a[i-1].b)&&(pd(a[i].a-a[i-1].a,abs(a[i].b-a[i-1].b)))))
{
continue;
}
puts("No");
return;
}
puts("Yes");
}
signed main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
int T=read();
while(T--) solve();
return 0;
}
B. 小 Z 爱划分 (partition)
注意:下文 \(\text{(爱)}\) 指艾弗森括号。
首先考虑把 \(O(n^2)\) dp 做出来。
设 \(s_i=a[1] \oplus a[2] \oplus \dots \oplus a_i\)。
得到 \(dp\) 柿子:
设 \(x=s_i \oplus s_j\)。
我们从 \(x\) 入手,考察每一个 \(j\) 的贡献。
因为异或满足很多神奇性质,考虑拆位处理。
现在设 \(x=2^{p_1}+2^{p_2}+\dots + 2^{p_k}\)。其中 \(p_i\) 互不相同。
又因为 \(x^2= x \times x\)
直接拆括号:
当然这样就可以了。
如果你觉得复杂度稍高,可以继续优化:
复杂度减半。
回到我们的 dp 式子:
发现
因为 \(x=s_i \oplus s_j\),若 \(x\) 的第 \(k\) 位为 \(1\),则 \(s_i\) 与 \(s_j\) 的第 \(k\) 位必须不同,等价于 \(s_i\) 与 \(s_j\) 的第 \(k\) 位的值的异或结果为 \(1\)。
同理可得
意义为 \(x\) 的第 \(p\) 位和第 \(q\) 位同时为 \(1\)。
则 $dp_i=A+B $ 即可解出。
现在考虑重新设计 dp 状态:
设原 dp 为 f。
设 \(k,p,q\) 为 \(s_i\) 的二进制表示的第 \(k,p,q\) 位。再设 \(2_k,2_p,2_q\) 表示 \(s_i\) 的二进制中这一位的数是什么。
我们设:
\(dp_1[k][0/1]\) 为所有 \((s_i)_2\) 的第 \(k\) 位的值为 \(0/1\),所有 \(f_i\) 的和。
\(dp_2[p][q][0/1][0/1]\) 为所有 \((s_i)_2\) 的第 \(p\) 位的值为 \(0/1\) 且第 \(q\) 位的值为 \(0/1\),所有 \(f_i\) 的和。
形式化定义
那么
算完 \(f_i\) 之后按照定义更新 \(dp_1\) \((\log V)\) 和 \(dp_2\) \((\log^2 V)\) 即可。
答案即可 \(O(\frac{n\log^2{V}}{2})\) 求出。
注意二的次幂算出后立刻取模,否则炸 long long。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar() \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt) \
? EOF \
: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n;
int a[1<<20];
const int N=1<<20;
const int mod=1e9+7;
int sum[N];
int dp[32][2];
int dp2[32][32][2][2];
const int base=30;
void solve()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) sum[i]=sum[i-1]^a[i];
memset(dp,0,sizeof(dp));
memset(dp2,0,sizeof(dp2));
for(int i=0;i<=base;i++) dp[i][0]=1;
for(int i=0;i<=base;i++)
for(int j=i+1;j<=base;j++)
dp2[i][j][0][0]=1;
int ans=0;
for(int i=1;i<=n;i++)
{
ans=0;
for(int j=0;j<=base;j++)
ans+=dp[j][((sum[i]>>j)&1)^1]*(((1ll<<(j<<1)))%mod)%mod,ans%=mod;
for(int j=0;j<=base;j++)
for(int k=j+1;k<=base;k++)
{
ans+=dp2[j][k][((sum[i]>>j)&1)^1][((sum[i]>>k)&1)^1]*((1ll<<(j+k+1))%mod);
ans%=mod;
}
for(int j=0;j<=base;j++)
dp[j][(sum[i]>>j)&1]+=ans,dp[j][(sum[i]>>j)&1]%=mod;
for(int j=0;j<=base;j++)
for(int k=j+1;k<=base;k++)
{
dp2[j][k][(sum[i]>>j)&1][(sum[i]>>k)&1]+=ans;
dp2[j][k][(sum[i]>>j)&1][(sum[i]>>k)&1]%=mod;
}
}
cout<<ans<<"\n";
}
signed main()
{
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
int T=read();
while(T--) solve();
return 0;
}
C. 小 Z 爱优化 (opti)
借鉴 CF1919F2 的思想。
我们尝试固定最小值,让最大值最小。
容易发现可能更新答案的最小值只会有 \(2n-1\) 个。
直接对 \(a\) 建线段树,对于每个节点维护 \(dp[p][1/0][1/0]\) 表示 \(p\) 节点所管辖的区间 \([l,r]\) 中 \(l\) \([\)是\(/\)否\(]\) 和 \(l-1\) 在同一段,\(r\) \([\)是\(/\)否\(]\) 和 \(r+1\) 在同一段。
初始化:
\(a[0]=a[n+1]=inf\)
叶子节点:
dp[p][0][0]=a[l]
dp[p][0][1]=a[l]+a[l+1]
dp[p][1][0]=a[l-1]+a[l]
dp[p][1][1]=inf
非叶子节点直接 pushup,固定左右两端点状态,枚举中间状态,取最小值。
void pushup(int p)
{
dp[p][0][0]=min({max(dp[lp][0][0],dp[rp][0][0]),max(dp[lp][0][1],dp[rp][1][0])});
dp[p][0][1]=min({max(dp[lp][0][0],dp[rp][0][1]),max(dp[lp][0][1],dp[rp][1][1])});
dp[p][1][0]=min({max(dp[lp][1][0],dp[rp][0][0]),max(dp[lp][1][1],dp[rp][1][0])});
dp[p][1][1]=min({max(dp[lp][1][0],dp[rp][0][1]),max(dp[lp][1][1],dp[rp][1][1])});
}
每次固定最小值 \(x\),调用 \(dp[rt][0][0]\) 即可得到最小的最大值。
然后做所有 \(a[i]=x\) 的 \(i\) 的单点修改和 \(a[i]+a[i-1]=x\) 的 \(i\) 和 \(i-1\) 的单点修改。
实现时将线段树上叶子 \(p\)(管辖 \([i,i]\))的 \(dp[p][0][0]\) 设为 \(inf\) 即可。
还需要判断若 \(a[i-1]+a[i]<=x\),则令 \(dp[p][1][0]=inf\);若 \(a[i]+a[i+1]<=x\),则令 \(dp[p][0][1]=inf\)。表示不能这么选。
发现我们只会修改最多 \(n+2(n-1)\) 次。复杂度大常数 \(O(n \log n)\),可以通过。
注意我们需要将最小值去重,否则可以被卡到 \(O(n^2 \log n)\)。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n;
int a[1<<20];
int b[1<<20];
const int N=2e5+5,inf=1e18+1;
vector<int> v[N<<1];
vector<int> ans;
unordered_map<int,int> id;
#define lp (p<<1)
#define rp ((p<<1)|1)
int dp[N<<2][2][2];
void pushup(int p)
{
dp[p][0][0]=min({max(dp[lp][0][0],dp[rp][0][0]),max(dp[lp][0][1],dp[rp][1][0])});
dp[p][0][1]=min({max(dp[lp][0][0],dp[rp][0][1]),max(dp[lp][0][1],dp[rp][1][1])});
dp[p][1][0]=min({max(dp[lp][1][0],dp[rp][0][0]),max(dp[lp][1][1],dp[rp][1][0])});
dp[p][1][1]=min({max(dp[lp][1][0],dp[rp][0][1]),max(dp[lp][1][1],dp[rp][1][1])});
}
void build(int l,int r,int p)
{
memset(dp[p],0,sizeof(dp[p]));
if(l==r)
{
dp[p][0][0]=a[l];
dp[p][0][1]=(l==n?inf:(a[l]+a[l+1]));
dp[p][1][0]=(l==1?inf:(a[l]+a[l-1]));
dp[p][1][1]=inf;
return;
}
int mid=(l+r)>>1;
build(l,mid,lp);
build(mid+1,r,rp);
pushup(p);
}
bool vis[N];
void change(int l,int r,int x,int minn,int p)
{
if(l==r)
{
if(a[l]<=minn) dp[p][0][0]=inf;
if(l>1&&a[l]+a[l-1]<=minn) dp[p][1][0]=inf;//,change(1,n,l-1,minn,1);
if(l<n&&a[l]+a[l+1]<=minn) dp[p][0][1]=inf;//,change(1,n,l+1,minn,1);
if(dp[p][0][0]==dp[p][1][0]&&dp[p][1][0]==dp[p][0][1]&&dp[p][0][0]==inf) vis[p]=1;
return;
}
int mid=(l+r)>>1;
if(x<=mid) change(l,mid,x,minn,lp);
else change(mid+1,r,x,minn,rp);
pushup(p);
}
void Deb(int l,int r,int p)
{
cout<<"["<<l<<","<<r<<"]"<<" p="<<p<<"\n";
cout<<"dp[p][0][0]="<<dp[p][0][0]<<"\n";
cout<<"dp[p][0][1]="<<dp[p][0][1]<<"\n";
cout<<"dp[p][1][0]="<<dp[p][1][0]<<"\n";
cout<<"dp[p][1][1]="<<dp[p][1][1]<<"\n\n";
int mid=(l+r)>>1;
if(l==r) return;
Deb(l,mid,lp);
Deb(mid+1,r,rp);
}
void solve(int T)
{
for(int i=1;i<=n;i++) vis[i]=0;
id.clear();
ans.clear();
n=read();
a[0]=-inf;
a[n+1]=inf;
for(int i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
sort(b+1,b+1+n);
b[0]=-inf;
b[n+1]=inf;
int tot=0;
for(int i=1;i<=n;i++)
if(b[i]!=b[i-1]) id[b[i]]=++tot,ans.push_back(b[i]);
for(int i=1;i<=n;i++)
v[id[a[i]]].push_back(i);
for(int i=2;i<=n;i++) if(!id[a[i-1]+a[i]]) id[a[i-1]+a[i]]=++tot,ans.push_back(a[i-1]+a[i]);
sort(ans.begin(),ans.end());
int totnum=tot;
for(int i=2;i<=n;i++)
{
v[id[a[i]+a[i-1]]].push_back(i-1);
v[id[a[i]+a[i-1]]].push_back(i);
}
int ans_min=inf;
build(1,n,1);
for(int i=1;i<=n;i++) vis[i]=0;
for(int i:ans)
{
int nw=id[i];
if(dp[1][0][0]>=inf) break;
ans_min=min(ans_min,dp[1][0][0]-i);
for(int pos:v[nw]) if(!vis[pos]) change(1,n,pos,i,1);
}
for(int i=0;i<=tot;i++) v[i].clear();
ans.clear();
cout<<ans_min<<"\n";
}
signed main()
{
int T=read();
for(int i=1;i<=T;i++)solve(i);
return 0;
}
Code:
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/p/19143922
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号