CSP 模拟 33

A 商品

对于一个数对 \((x,y)[x<y]\),无论怎样选择区间,这个数对的贡献都是 \(y-x\),那么考虑对于所有数对,把 \(x\)\(y\) 分别放到一起,排好序。如果当前区间确定,在两个数组二分一下,前缀和就能处理出答案。
显然区间越大越好,所以只考虑区间左端点,考虑只对于点对 \((x,y)\) 来说,\(l\le x\) 相同,\(r\ge y\) 相同,所以可以取到 \(x\)\(y-d\) 两个左端点。一次检查每个端点计算答案即可。
赛时端点没有考虑右边的贡献,并且一开始想数据结构,后来发现优先队列更好,然后就没思考性质,80pts,但是确实是对的。
直接大力分讨 \(x\)\(y\) 与区间的关系,拿优先队列存储,然后根据单调性一个一个扔,巨麻烦。

#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int 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;}
const int N=4e5+10;
int n,d,a[N],temp[N],ans=0,ans2,ans3,ans4,ans5,size,num,Ne[N];
int l=0,r=0,L,R;
bool vis[N];
struct NODE{
    int x,y,id;
    bool operator<(const NODE &a)const{return x>a.x;}
};
struct WU{
    int x,y,id;
    bool operator<(const WU&a)const{return y>a.y;}
};
std::priority_queue<NODE> q1,q2,q3;
std::priority_queue<WU> qf,q4,q5;
signed main(){
    freopen("goods.in","r",stdin);freopen("goods.out","w",stdout);
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    n=read(),d=read();
    for(int i=1;i<=n;++i)temp[i]=a[i]=read();
    std::sort(temp+1,temp+n+1);
    l=r=temp[1];L=R=1;
    int top=0;
    for(int i=1;i<n;++i){
        int x=a[i],y=a[i+1];if(x>y)std::swap(x,y);
        Ne[++top]=x,Ne[++top]=y-d;
        q1.push({x,y,i});
    }
    std::sort(Ne+1,Ne+top+1);
    while(L<=top){
        l=Ne[L];
        l=std::max(l,0ll);r=l+d;
        while(!q1.empty()){
            auto it=q1.top();q1.pop();
            int x=it.x,y=it.y,id=it.id;
            if(x>=r&&y>=r){
                q1.push({x,y,id});break;
            }
            if(x>=l&&x<=r&&y>=r){
                q2.push({x,y,id});ans2+=x;size++;
                qf.push({x,y,id});
                vis[id]=1;
                continue;
            }
            if(x>=l&&x<=r&&y>=l&&y<=r){
                q3.push({x,y,id});ans3+=y-x;
                continue;
            }
            if(x<=l&&y>=r){
                q4.push({x,y,id});ans4++;
                continue;
            }
            if(x<=l&&y>=l){
                q5.push({x,y,id});ans5+=y;
                continue;
            }
        }
        while(!q2.empty()){
            auto it=q2.top();q2.pop();
            int x=it.x,y=it.y,id=it.id;
            if(!vis[id])continue;
            ans2-=x;size--;vis[id]=0;
            if(x>=l&&x<=r&&y>=r){
                q2.push({x,y,id});ans2+=x;size++;
                vis[id]=1;
                break;
            }
            if(x>=l&&x<=r&&y>=l&&y<=r){
                q3.push({x,y,id});ans3+=y-x;
                continue;
            }
            if(x<=l&&y>=r){
                q4.push({x,y,id});ans4++;
                continue;
            }
            if(x<=l&&y>=l&&y<=r){
                q5.push({x,y,id});ans5+=y;
                continue;
            }
        }
        while(!qf.empty()){
            auto it=qf.top();qf.pop();
            int x=it.x,y=it.y,id=it.id;
            if(!vis[id])continue;
            ans2-=x;size--;vis[id]=0;
            if(x>=l&&x<=r&&y>=r){
                qf.push({x,y,id});ans2+=x;size++;
                vis[id]=1;
                break;
            }
            if(x>=l&&x<=r&&y>=l&&y<=r){
                q3.push({x,y,id});ans3+=y-x;
                continue;
            }
        }
        while(!q3.empty()){
            auto it=q3.top();q3.pop();
            int x=it.x,y=it.y,id=it.id;ans3-=y-x;
            if(x>=l&&x<=r&&y>=l&&y<=r){
                q3.push({x,y,id});ans3+=y-x;
                break;
            }
            if(x<=l&&y>=r){
                q4.push({x,y,id});ans4++;
                continue;
            }
            if(x<=l&&y>=l&&y<=r){
                q5.push({x,y,id});ans5+=y;
                continue;
            }
        }
        while(!q4.empty()){
            auto it=q4.top();q4.pop();
            int x=it.x,y=it.y,id=it.id;ans4--;
            if(x<=l&&y>=r){
                q4.push({x,y,id});ans4++;
                break;
            }
            if(x<=l&&y>=l&&y<=r){
                q5.push({x,y,id});ans5+=y;
                continue;
            }
        }
        while(!q5.empty()){
            auto it=q5.top();q5.pop();
            int x=it.x,y=it.y,id=it.id;ans5-=y;
            if(x<=l&&y>=l&&y<=r){
                q5.push({x,y,id});ans5+=y;
                break;
            }
        }
        num=q5.size();
        int res=size*r-ans2+ans3+ans4*(r-l)+ans5-num*l;
        ans=std::max(ans,res);
        L++;
    }
    std::cout<<ans<<'\n';
}

B 价值

究极分讨树形 DP,但是状态对了也挺好写。
如果没有神必叶子边,直接 DP 就做完了,考虑给 DP 加上一些限制,\(f_{u,0/1,0/1,0/1}\) 表示在 \(u\) 的子树中,\(u\) 是否匹配,最左端的叶子是否和前一个叶子匹配,最右端的叶子是否和后一个叶子匹配。答案就是 \(f_{u,0,0,0}+f_{u,0,1,1}+f_{u,1,0,0}+f_{u,1,1,1}\)
考虑子树合并的转移,对于所有节点,有初始状态 \(f_{u,0,0,0}=1\),对于叶子,有 \(f_{u,1,0,1}=f_{u,1,1,0}=1\)。对于第一个儿子节点,直接继承转移。
对于一般情况,子节点的状态必须要与合并状态的右端点状态相匹配,知道这个之后就好写多了,具体转移看代码(最短解)。

#include<bits/stdc++.h>
#define fo(i,s,j) for(int i=s;i<=j;++i)
#define int long long
inline int read(){char ch=getchar();int 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;}
const int N=1e5+10,mod=998244353;
inline int mo(int x){return x<mod?x:x%mod;}
int n,f[N][2][2][2],la[2][2][2],ans;
std::vector<int> e[N];
inline void dfs(int u){
    f[u][0][0][0]=1;
    if(!e[u].size()){f[u][1][1][0]=f[u][1][0][1]=1;return ;}
    for(int i=0;i<e[u].size();++i){
        int v=e[u][i];dfs(v);
        if(!i){fo(l,0,1)fo(r,0,1)f[u][0][l][r]=mo(f[v][0][l][r]+f[v][1][l][r]),f[u][1][l][r]=f[v][0][l][r];continue;}
        memcpy(la,f[u],sizeof(la));
        fo(l,0,1)fo(r,0,1)f[u][0][l][r]=mo(mo(la[0][l][r]*(f[v][0][r][r]+f[v][1][r][r]))+mo(la[0][l][r^1]*(f[v][0][r^1][r]+f[v][1][r^1][r])));
        fo(l,0,1)fo(r,0,1)f[u][1][l][r]=mo(mo(la[0][l][r]*f[v][0][r][r])+mo(la[0][l][r^1]*f[v][0][r^1][r])+mo(la[1][l][r]*(f[v][0][r][r]+f[v][1][r][r]))+mo(la[1][l][r^1]*(f[v][0][r^1][r]+f[v][1][r^1][r])));
    }
}
signed main(){
    freopen("value.in","r",stdin);freopen("value.out","w",stdout);
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    n=read();fo(i,2,n){int x=read();e[x].emplace_back(i);}dfs(1);
    std::cout<<mo(f[1][0][0][0]+f[1][0][1][1]+f[1][1][0][0]+f[1][1][1][1])<<'\n';

}

C 货币

网络流切糕模型,以后系统地学一下。

D 资本

生成函数,以后系统地学一下。

总结

这场成策略大师了,直接想优先队列能做后就不想别的了,你真以为正解需要六个优先队列,然后对拍调试三个小时,后面暴力都没时间拼。
T2 这种带点构造博弈感觉的树形 DP 就一点都不会,看题解也看不明白,该多练 DP 了。

posted @ 2024-09-26 19:38  Ishar-zdl  阅读(34)  评论(1)    收藏  举报