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\) 柿子:

\[dp_i=\sum_{j=0}^{i-1} {dp_j\times((s_i \oplus s_j)^2)} \]

\(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\)

\[\begin{equation*} \begin{aligned} x^2 &= x \times x\\ &=(2^{p_1}+2^{p_2}+\dots + 2^{p_k}) \times (2^{p_1}+2^{p_2}+\dots + 2^{p_k}) \\ \end{aligned} \end{equation*}\]

直接拆括号:

\[=\sum_{i=1}^{k} \sum_{j=1}^k 2^{p_i+p_j} \]

当然这样就可以了。

如果你觉得复杂度稍高,可以继续优化:

\[ \begin{equation*} \begin{aligned} &=\sum_{i=1}^k (2^{p_i})^2 + \sum_{i=1}^{k} \sum_{j=i+1}^k 2\times2^{p_i+p_j} \\ &=\sum_{i=1}^k (2^{p_i})^2 + \sum_{i=1}^{k} \sum_{j=i+1}^k 2^{p_i+p_j+1}\\ \end{aligned} \end{equation*} \]

复杂度减半。

回到我们的 dp 式子:

\[\begin{equation*} \begin{aligned} dp_i &=\sum_{j=0}^{i-1} {(dp_j\times(\sum_{i=1}^k (2^{p_i})^2 + \sum_{i=1}^{k} \sum_{j=i+1}^k 2^{p_i+p_j+1}))} \\ &= (\sum_{j=0}^{i-1} dp_j\times\sum_{i=1}^k (2^{p_i})^2 )+( \sum_{j=0}^{i-1}dp_j \times (\sum_{i=1}^{k} \sum_{j=i+1}^k 2^{p_i+p_j+1}))\\ &= (A)+(B)\\ \end{aligned} \end{equation*}\]

发现

\[\begin{equation*} \begin{aligned} A&= \sum_{j=0}^{i-1} dp_j\times\sum_{i=1}^k (2^{p_i})^2 \\ &= \sum_{k=0}^{30} 2^{2k} \sum_{j=0}^{i-1}dp_j [ (x >>k)\&1 \text{(爱)} ] \\ &= \sum_{k=0}^{30} 2^{2k} \sum_{j=0}^{i-1}dp_j [((s_i>>k)\&1) \oplus((s_j>>k)\&1)\text{(爱)} ]\\ \end{aligned} \end{equation*} \]

因为 \(x=s_i \oplus s_j\),若 \(x\) 的第 \(k\) 位为 \(1\),则 \(s_i\)\(s_j\) 的第 \(k\) 位必须不同,等价于 \(s_i\)\(s_j\) 的第 \(k\) 位的值的异或结果为 \(1\)

同理可得

\[\begin{equation*} \begin{aligned} B&= \sum_{j=0}^{i-1}dp_j \times (\sum_{i=1}^{k} \sum_{j=i+1}^k 2^{p_i+p_j+1})\\ &= \sum_{p=0}^{30} \sum_{q=i+1}^{30} 2^{p+q+1} \sum_{j=0}^{i-1} dp_j[((x>>p)\&1)\&((x>>q)\&1)\text{(爱)}]\\ &= \sum_{p=0}^{30} \sum_{q=i+1}^{30} 2^{p+q+1} \sum_{j=0}^{i-1} dp_j[(((s_i>>k)\&1) \oplus((s_j>>k)\&1))\&(((s_i>>k)\&1) \oplus((s_j>>k)\&1))\text{(爱)}] \end{aligned} \end{equation*}\]

意义为 \(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\) 的和。

形式化定义

\[\begin{equation*} \begin{aligned} &dp_1[k][0] = \sum_{i=0} f_i[2_k=0\text{(爱)}]\\ &dp_1[k][1] = \sum_{i=0} f_i[2_k=1\text{(爱)}]\\ &\\ &dp_2[p][q][0][0]=\sum_{i=0} f_i[2_p=0\text{(爱)}][2_q=0\text{(爱)}]\\ &dp_2[p][q][0][1]=\sum_{i=0} f_i[2_p=0\text{(爱)}][2_q=1\text{(爱)}]\\ &dp_2[p][q][1][0]=\sum_{i=0} f_i[2_p=1\text{(爱)}][2_q=0\text{(爱)}]\\ &dp_2[p][q][1][1]=\sum_{i=0} f_i[2_p=1\text{(爱)}][2_q=1\text{(爱)}]\\ \end{aligned} \end{equation*}\]

那么

\[\begin{equation*} \begin{aligned} f[i]&=\sum_{k=0}^{30} 2^{2k} dp_1[k][(s_i>>k) \oplus 1]\\ &+\sum_{p=0}^{30} \sum_{q=i+1}^{30} 2^{p+q+1}dp_2[p][q][(s_i>>p) \oplus 1][(s_i>>q) \oplus 1] \end{aligned} \end{equation*}\]

算完 \(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)

原题 luogu P9893(有负数)

借鉴 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:

posted @ 2025-10-15 17:46  Wy_x  阅读(37)  评论(1)    收藏  举报