『模拟赛』暑假集训CSP提高模拟17

Rank

image

A. 符号化方法初探

原[ABC081D] Non-decreasing

挺水的,不过赛时想了错解。

赛时做法是都塞到一个 set 里一遍推过去,如果比上一个小就 lower_bound 找一个最接近差的数加上,不过它存在比较大的问题。

首先全是负判不了,会进入死循环;其次用 map 存下标也会出现存在两个相同的值,一个替换另一个后值更改,此时找到的下标对应的值并不是我们想要的值。

但是还是对他抱一些希望,如果有能调出来的大佬请帮忙看下感谢qwq。

赛时代码

更改前

#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx int
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
const int Ratio=0;
const int N=1e5+5;
int n,m;
ll a[N];
ll q1[N],q2[N];
map<ll,ll>mp;
set<ll>st;
set<ll>::iterator it;
namespace Wisadel
{
    short main()
    {
        // freopen(".in","r",stdin),freopen(".out","w",stdout);
        n=qr;
        fo(i,1,n) a[i]=qr,st.insert(a[i]),mp[a[i]]=i;
        fo(i,2,n)
        {
            while(a[i]<a[i-1])
            {
                it=st.lower_bound(a[i-1]-a[i]);
                if(it==st.end()) --it;
                q1[++m]=mp[*it],q2[m]=i;
                st.erase(a[i]);
                a[i]+=*it;
                st.insert(a[i]);
                mp[a[i]]=i;
            }
        }
        printf("%d\n",m);
        fo(i,1,m)
            printf("%lld %lld\n",q1[i],q2[i]);
        return Ratio;
    }
}
int main(){return Wisadel::main();}

更改后

#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx int
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
const int Ratio=0;
const int N=1e5+5;
int n,m;
ll a[N];
ll q1[N],q2[N];
map<ll,ll>mp;
set<ll>st;
set<ll>::iterator it;
namespace Wisadel
{
    short main()
    {
        // freopen(".in","r",stdin),freopen(".out","w",stdout);
        n=qr;
        ll maxx=-1e9,minn=1e9;
        fo(i,1,n) a[i]=qr,st.insert(a[i]),mp[a[i]]=i,maxx=max(maxx,a[i]),minn=min(minn,a[i]);
        if(maxx+minn>0)
        {
            fo(i,2,n)
            {
                while(a[i]<a[i-1])
                {
                    it=st.lower_bound(a[i-1]-a[i]);
                    if(it==st.end()) --it;
                    q1[++m]=mp[*it],q2[m]=i;
                    st.erase(a[i]);
                    a[i]+=*it;
                    st.insert(a[i]);
                    mp[a[i]]=i;
                }
            }
        }
        else
        {
            fu(i,n-1,1)
            {
                // cout<<i<<' '<<i+1<<":"<<a[i]<<' '<<a[i+1]<<endl;
                while(a[i]>a[i+1])
                {
                    // cout<<i<<' '<<a[i]<<' '<<a[i+1]<<endl;
                    it=st.lower_bound(a[i+1]-a[i]);
                    if(a[i+1]-a[i]!=*it&&it!=st.begin()) --it;
                    // cout<<"$%^&*(&^%$#)"<<*it<<' '<<mp[*it]<<endl;
                    q1[++m]=mp[*it],q2[m]=i;
                    st.erase(a[i]);
                    a[i]+=*it;
                    st.insert(a[i]);
                    mp[a[i]]=i;
                }
            }
        }
        printf("%d\n",m);
        fo(i,1,m)
            printf("%lld %lld\n",q1[i],q2[i]);
        return Ratio;
    }
}
int main(){return Wisadel::main();}

正解是若全不为负就 n-1 次碾过去,每次让 i 加上前一个的值;若全不为正就倒序同上;否则比较极值的绝对值大小,最大值的较大就让所有除最大值外的数加上最大值,然后同情况 1,最小值的较大就让所有除最小值外的数加上最小值,然后同情况 2。

点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx int
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
const int Ratio=0;
const int N=2e5+5;
const int mod=998244353;
int n,m;
struct rmm
{
    ll x,id;
}a[N];
bool cmp(rmm a,rmm b){return a.x<b.x;}
namespace Wisadel
{
    short main()
    {
        // freopen(".in","r",stdin),freopen(".out","w",stdout);
        n=qr;
        fo(i,1,n) a[i].x=qr,a[i].id=i;
        sort(a+1,a+1+n,cmp);
        if(a[1].x>=0)
        {
            printf("%d\n",n-1);
            fo(i,1,n-1) printf("%d %d\n",i,i+1);
        }
        else if(a[n].x<=0)
        {
            printf("%d\n",n-1);
            fu(i,n,2) printf("%d %d\n",i,i-1);
        }
        else
        {
            printf("%d\n",2*n-2);
            if(abs(a[1].x)<=a[n].x)
            {
                fo(i,1,n) if(i!=a[n].id) printf("%d %d\n",a[n].id,i);
                fo(i,1,n-1) printf("%d %d\n",i,i+1);
            }
            else
            {
                fo(i,1,n) if(i!=a[1].id) printf("%d %d\n",a[1].id,i);
                fu(i,n,2) printf("%d %d\n",i,i-1);
            }
        }
        return Ratio;
    }
}
int main(){return Wisadel::main();}

B. 无标号 Sequence 构造

原[GDKOI2023 提高组] 矩阵

一道非常有学习意义的题。

一眼脑残题,然后看到数据范围 \(n\le 3000\) 蒙了,没想到什么好办法,所以还是 \(\mathcal{O(n^3)}\) 硬莽拿下 50pts。

正解是随机一个 \(1\times n\) 的矩阵,这样就缩小了矩阵相乘的规模,从而将复杂度降低到 \(\mathcal{O(N^2)}\) 级别。

写了篇题解求赞捏。

点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)

const int Ratio=0;
const int N=3e3+5;
const int mod=998244353;
int T,n;
int a[N][N],b[N][N],c[N][N],d[2][N],e[2][N],f[2][N],g[2][N];
namespace Wisadel
{
    short main()
    {
        scanf("%d",&T);srand(time(0));
        while(T--)
        {
            scanf("%d",&n);bool can=1;
            fo(i,1,n) fo(j,1,n) scanf("%d",&a[i][j]);
            fo(i,1,n) fo(j,1,n) scanf("%d",&b[i][j]);
            fo(i,1,n) fo(j,1,n) scanf("%d",&c[i][j]);
            fo(i,1,n) d[1][i]=rand(),e[1][i]=0,f[1][i]=0,g[1][i]=0;
            fo(j,1,n) fo(k,1,n) e[1][j]=(e[1][j]+1ll*d[1][k]*a[k][j]%mod)%mod;
            fo(j,1,n) fo(k,1,n) f[1][j]=(f[1][j]+1ll*e[1][k]*b[k][j]%mod)%mod;
            fo(j,1,n) fo(k,1,n) g[1][j]=(g[1][j]+1ll*d[1][k]*c[k][j]%mod)%mod;
            fo(i,1,n) if(f[1][i]!=g[1][i]){can=0;break;}
            printf(can?"Yes\n":"No\n");
        }
        return Ratio;
    }
}
int main(){return Wisadel::main();}

C. 无标号 Multiset 构造

需要先打半个小时表预处理出来不同的 m 值,所以只拿了 5pts 的样例分。

D. 有限制的构造

原[ABC364E] Maximum Glutton

也很有学习意义的一题。

如果不看数据范围,是一个很经典的二维费用背包问题,\(f_{i,j,k}\) 表示到第 \(i\) 个游戏,画质和为 \(j\),不可玩度为 \(k\) 最多选几个游戏,最终答案为 \(f_{n,A,B}\),当然第一维完全可以省略。

但是,这道题两个费用的上线都是 \(10^4\) 级别的,空间上就算省掉了一维复杂度还是 \(\mathcal{O(n^2)}\),就算开 short 也无法承受,时间复杂度为 \(\mathcal{O(nV^2)}\) 同样无法实现。

所以需要转换思路。

首先一种,因为 \(n\le 80\) 范围极小,所以我们可以选择退火,(但是不会所以不考虑。

第二,我们可以交换一维 dp 数组的维度,将范围较小的值域计入一维,将范围较大的费用计入值域,此时我们的 dp 数组的含义变成了:\(f_{i,j,k}\) 表示到第 \(i\) 个游戏,玩了 \(j\) 个,画质和为 \(k\) 时的最小不可玩度。这样一来,我们的空间复杂度和时间复杂度都变为了 \(\mathcal{O(n^2V)}\),最后统计答案只需要倒序遍历一遍 \(j\) 维找到值满足 \(\le B\) 的即可。

还要注意的一点,我们可以最后超出界限一个游戏,想象一下就是:背包目前塞不下一个物品,我们可以敞口再放一个。不过这样的前提是 \(n\) 个游戏没有全部都玩(致敬 lxyt

点击查看代码(未优化,开 short 过的)
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx int
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
const int Ratio=0;
const int N=1e4+5;
int n,A,B;
short a[N],b[N],f[88][88][N];
namespace Wisadel
{
    short main()
    {
        // freopen(".in","r",stdin),freopen(".out","w",stdout);
        n=qr,A=qr,B=qr;
        fo(i,1,n) a[i]=qr,b[i]=qr;
        memset(f,0x3f,sizeof f);
        fo(i,0,n) f[i][0][0]=0;
        fo(i,1,n) fo(j,1,i) fo(k,0,A)
        {
            if(k>=a[i]) f[i][j][k]=min(f[i][j][k],short(f[i-1][j-1][k-a[i]]+b[i]));
            f[i][j][k]=min(f[i][j][k],f[i-1][j][k]);
        }
        fu(i,n,0) fo(j,0,A) if(f[n][i][j]<=B)
        {
            printf("%d\n",i+(i!=n));
            return Ratio;
        }
        return Ratio;
    }
}
int main(){return Wisadel::main();}

这次也是收获很多的一场模拟赛啊,能学到很多专题里学不到的小细节。

博客写到 T4 差点坠机了,还好 yuen 告诉我博客园有自动备份,感谢博客园!同时感谢在榜首把我的分块莫队挂了两天!

赛时一直受困于在线测评的 sb 机制:超过给定内存的一半自动 RE,导致我卡在 T2 一个小时,最后还被迫改小了数组,(虽然改不改一个分。


完结撒花~

另外

\(\Huge{七夕节快乐!}\)

\(\small{能在学 OI 时找到另一半吗www}\)

posted @ 2024-08-10 17:30  DrRatio  阅读(33)  评论(0编辑  收藏  举报