2024暑假集训测试3

前言

image

T4 赛时想了 \(3\) 个小时打出来了,但是计算内存的时候开了两个数组忘记 \(\times 2\) 了,当时为了防止炸掉多开了一点点,就爆零了,小开一点点就过了,非常难受。

这次比赛好像一个部分分没打出来,数据范围给的也不像能有部分分的。

T1 超市抢购

签到题,注意给的随机化种子要基于 unsigned int,不要 #define int long long,但答案需要开 long long

T2 核酸检测

定义 \(f_i\) 为在 \(i\) 处喊一声,且 \(l\le i\) 的都被喊道最少需要喊多少次,\(g_i\) 为对应的方案数。

\(f_i=\min\limits_{0\le j\le i-1}\{f_j+1\}\)\(j\) 能转移到 \(i\) 当且仅当不存在任何一组 \(j+1\le l\le r\le i-1\)

对于每一个 \(l\),处理出其 \(to_l\) 表示左端点为 \(l\) 的所有人中右端点最靠左的 \(r\),故有当 \(\min\limits_{k=i+1}^{j-1}to_k\le j\) 时,方程可以转移。

故在转移的过程中,对于一个 \(f_i\),枚举出其所有能转移到的 \(j\),显然当 \(j\) 无法被转移到的时候 \(j+1\) 一定转移不到。

至于 \(g_i\),在转移 \(f_i\)\(f_j\) 的过程同时,若 \(f_j+1>f_i\),则领 \(g_j\) 集成 \(f_i\),若 \(f_j=f_i+1\),说明有不同的方案,则 \(g_j+=g_i\)

最后统计答案,对于最靠右的 \(l\),定义为 \(maxl\),同理定义 \(maxr\),知道这段区间内必须是要喊一次的,那么对于 \(f_{maxl}\sim f_{maxr}\) 都是合法的,直接取其中的最小值为答案;方案书为 \(\sum\limits_{i=maxl}^{maxr}[f_i=ans]\times g_i\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1030,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,to[N],maxl,maxr,f[N],g[N],ans,sum;
signed main()
{
    read(n);
    memset(to,0x3f,sizeof(to));
    for(int i=1,l,r;i<=n;i++)
        read(l),read(r),
        to[l]=min(to[l],r),
        maxl=max(maxl,l),
        maxr=max(maxr,r);
    memset(f,0x3f,sizeof(f));
    f[0]=0,g[0]=1;
    for(int i=0;i<=maxr;i++)
    {
        int r=to[i+1];
        for(int j=i+1;j<=r;j++)
        {
            if(f[i]+1<f[j])
                f[j]=f[i]+1,
                g[j]=g[i];
            else if(f[i]+1==f[j])
                (g[j]+=g[i])%=P;
            r=min(r,to[j]);
        }
    }
    ans=0x3f3f3f3f;
    for(int i=maxl;i<=maxr;i++)
    {
        if(f[i]<ans)
        {
            ans=f[i];
            sum=g[i];
        }
        else if(f[i]==ans)
            (sum+=g[i])%=P;
    }
    write(ans),puts(""),write(sum);
}

T3 七龙珠

对于每颗龙珠都可以是多少能量可以用背包 DP 求出,考虑只需处理能量 \(x\) 是否合法,所以用 bool 即可,考虑这样复杂度为 \(O(\sum\limits_{i=1}^7x_i\times m)\)\(7e9\) 级别,肯定过不去,考虑使用 bitset 优化,使其复杂度为 \(O(\dfrac{\sum\limits_{i=1}^7x_i\times m}{64})\),鉴于 bitset 为一个 \(01\) 串,联系状压里的知识,对于每个 \(b_i\)\(f|=(f<<b_i)\) 即可,更加方便。

之后 bfs 求出第 \(k\) 大方案即可。

全局最大一定为 \((1,1,1,1,1,1,1)\)\(1\) 指这颗龙珠的能量的排名(此处注意若 \(a_i<0\) 则能量从小到大排名),将其放到大根堆里,每次去除最大的一个,将这 \(7\) 位中枚举每一位使该位 \(+1\),其余不变,在把他让扔到堆里,第 \(k\) 次弹出的即使答案。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=5e5+10,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int m,k,a[10],b[N],len[10];
struct aa
{
    int val,p[10];
    friend bool operator < (const aa &x,const aa &y)
    {
        if(x.val!=y.val) return x.val<y.val;
        for(int i=1;i<=7;i++) 
            if(x.p[i]!=y.p[i])
                return x.p[i]<y.p[i];
        return 0;
    }
};
bitset<N>f;
map<aa,bool>v;
vector<int>e[10],ans;
priority_queue<aa>q;
void bfs()
{
    int sum=0;
    while(!q.empty()&&sum<k)
    {
        aa x=q.top();
        q.pop();
        ans.push_back(x.val);
        sum++;
        for(int i=1;i<=7;i++)
        {
            aa y=x;
            if(x.p[i]<len[i]-1)
            {
                y.p[i]++;
                y.val=x.val-e[i][x.p[i]]*a[i]+e[i][y.p[i]]*a[i];
                if(v[y]==1) continue;
                q.push(y);
                v[y]=1;
            }
        }
    }
    if(sum==k) write(ans[k-1]);
    else puts("Stop dreaming xm!");
}
signed main()
{
    read(m),read(k);
    for(int i=1;i<=7;i++) read(a[i]);
    for(int i=1,c;i<=7;i++)
    {
        read(c); 
        for(int j=1;j<=c;j++) read(b[j]);
        f[0]=1;
        for(int j=1;j<=c;j++)
            f|=(f<<b[j]);
        if(a[i]>0) 
        {
            for(int j=m;j>=0;j--)
                if(f[j]) 
                    e[i].push_back(j);
        }
        else 
        {
            for(int j=0;j<=m;j++)
                if(f[j])
                    e[i].push_back(j);
        }
        len[i]=e[i].size();
        for(int j=0;j<=m;j++) f[j]=0;
    }
    aa x;
    x.val=0;
    for(int i=1;i<=7;i++) x.p[i]=0,x.val+=e[i][0]*a[i];
    v[x]=1,q.push(x);
    bfs();
}

T4 龙珠游戏

不难看出要跑区间 DP,发现开了 \(3s\) 的时限和 \(1G\) 的内存,考虑 \(n^3\) 做法,思考第三维处理什么。

对于区间 \([l,r]\) 只能选 \(a_l\)\(a_r\),但 可以随便选,这就很不好处理, 每次一定选择的为最优的,从而思考 行动的本质就是阻止 的最优行动,不妨记录一个 \(k\) 表示 可以行动的次数,对于 的每次操作,若此时 \(k>0\),说明 可以阻止本次操作的进行,同时消耗一次行动次数,反之若 没有阻止 的操作,则 顺利拿到了 \(a_l,a_r\) 中的一个,同时 \(k+1\),即 获得一次行动次数。

已知结束状态 \(l=1,r=n,k=0\),初始状态不太好搞,递推不太好想,不妨记忆化搜索倒推进行递归处理,定义 \(f_{l,r,k}\) 表示 在进行操作,当前区间为 \([l,r]\)\(k\) 次行动次数, 最多能拿到多少;\(g_{l,r,k}\) 则表示 在进行操作,能使 最少拿到多少。鉴于 操作后一定轮到 ,而在 \(k>0\) 的前提下 有不操作和操作两种选择;同时根据贪心思想,当 \(r-l+1=k\) 时, 一次也不会再让 拿到,故此时 \(g_{l,r,k}=0\)。转移方程:

\[\begin{cases} f_{l,r,k}=\max(g_{l+1,r,k+1}+a_l,g_{l,r-1,k+1}+a_r)\\ g_{l,r,k}=\min(f_{l,r,k},g_{l+1,r,k-1},g_{l,r-1,k-1}) &k>0 \\ g_{l,r,k}=f_{l,r,k} &k=0 \\ g_{l,r,k}=0 &k=r-l+1 \\ \end{cases}\]

两个 \(dfs\) 轮流递归即可,复杂度 \(O(n^3)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=510;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,a[N],f[N][N][226],g[N][N][226];
int dfs(int l,int r,int k);
int dfs2(int l,int r,int k);
int dfs(int l,int r,int k) // 龙先不选,并获得一次优先选权。
{
    if(l>r) return 0;
    if(r-l+1==k) return 0;
    if(f[l][r][k]!=-1) return f[l][r][k];
    return f[l][r][k]=max(dfs2(l+1,r,k+1)+a[l],dfs2(l,r-1,k+1)+a[r]);
}
int dfs2(int l,int r,int k) // 龙要使用优先选择权。
{
    if(l>r) return 0;
    if(g[l][r][k]!=-1) return g[l][r][k];
    int ans=dfs(l,r,k); // 龙不使用优先选择权。
    if(k>=1) ans=min({ans,dfs2(l+1,r,k-1),dfs2(l,r-1,k-1)});
    return g[l][r][k]=ans;
}
signed main()
{
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    memset(f,-1,sizeof(f));
    memset(g,-1,sizeof(g));
    dfs(1,n,0);
    write(f[1][n][0]);
}

总结

posted @ 2024-07-11 19:22  卡布叻_周深  阅读(15)  评论(0编辑  收藏  举报