bzoj 5498: [2019省队联测]皮配【dp】

是个神仙dp……
参考:https://www.luogu.org/blog/xzz-233/solution-p5289
设f[i][j][k]是前i个有限制的城市,所有学校中选蓝色阵营有j人,有限制的学校中鸭派系有k人的方案数;g[i][j]是前i个没有限制的城市,蓝色阵营有j人的方案数;h[i][j]是前i个没有限制的学校,鸭派系有j人的方案数
f的转移枚举当前阵营
全dp出来之后,把gh前缀和处理,然后一段一段的和f合并加到ans上

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=2505,mod=998244353;
int T,n,c,c0,c1,d0,d1,k,m,ss,ans,b[N],s[N],f[N][N],f1[N][N],f2[N][N],g[N],h[N],ki[N],kp[N],v[N],ct[N],sum[N],sm[N];
vector<int>sc[N];
void jia(int &x,int y)
{
    if((x+=y)>=mod)
        x-=mod;
}
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
int main()
{
    T=read();
    while(T--)
    {
        memset(f,0,sizeof(f));
        memset(f1,0,sizeof(f1));
        memset(f2,0,sizeof(f2));
        memset(g,0,sizeof(g));
        memset(h,0,sizeof(h));
        memset(sm,0,sizeof(sm));
        memset(sum,0,sizeof(sum));
        memset(ct,0,sizeof(ct));
        for(int i=0;i<N;i++)
            sc[i].clear();
        ss=0,ans=0;
        n=read(),c=read(),c0=read(),c1=read(),d0=read(),d1=read();
        for(int i=1;i<=n;i++)
            b[i]=read(),s[i]=read(),sum[b[i]]+=s[i],ss+=s[i];
        m=k=read();
        for(int i=1;i<=n;i++)
            v[i]=-1;
        for(int i=1;i<=k;i++)
        {
            ki[i]=read(),kp[i]=read();
            ct[i]=b[ki[i]];
            sc[b[ki[i]]].push_back(ki[i]);
            v[ki[i]]=kp[i];
        }
        sort(ct+1,ct+1+m);
        m=unique(ct+1,ct+1+m)-ct-1;
        for(int i=1;i<=n;i++)
            if(v[i]!=-1)
                sm[b[i]]+=s[i];
        f[0][0]=1;
        for(int i=1,s1=0,s2=0;i<=m;i++)
        {
            s1=min(c0,s1+sum[ct[i]]),s2=min(d0,s2+sm[ct[i]]);
            for(int o=0;o<=1;o++)
            {
                for(int j=0;j<=s1;j++)
                    for(int k=0;k<=s2;k++)
                        f1[j][k]=f[j][k];
                for(int l=0,len=sc[ct[i]].size();l<len;l++)
                {
                    int x=sc[ct[i]][l];
                    for(int j=s1;j>=0;j--)
                        for(int k=s2;k>=0;k--)
                        {
                            if(o==0)
                            {
                                f1[j][k]=0;
                                if(v[x]!=1&&j>=s[x])
                                    jia(f1[j][k],f1[j-s[x]][k]);
                                if(v[x]!=0&&j>=s[x]&&k>=s[x])
                                    jia(f1[j][k],f1[j-s[x]][k-s[x]]);
                            }
                            else
                            {
                                if(v[x]==3)
                                    f1[j][k]=0;
                                if(v[x]!=2&&k>=s[x])
                                    jia(f1[j][k],f1[j][k-s[x]]);
                            }
                        }
                }
                if(o==0)
                {
                    int nw=sum[ct[i]]-sm[ct[i]];
                    for(int j=s1;j>=0;j--)
                        for(int k=s2;k>=0;k--)
                        {
                            if(j>=nw)
                                f1[j][k]=f1[j-nw][k];
                            else
                                f1[j][k]=0;
                        }
                    for(int j=0;j<=s1;j++)
                        for(int k=0;k<=s2;k++)
                            f2[j][k]=f1[j][k];
                }
                else
                {
                    for(int j=0;j<=s1;j++)
                        for(int k=0;k<=s2;k++)
                            f[j][k]=(f1[j][k]+f2[j][k])%mod;
                }
            }
        }
        g[0]=1;
        for(int i=1,smm=0;i<=c;i++)
            if(sc[i].empty()&&sum[i])
            {
                smm=min(c0,smm+sum[i]);
                for(int j=smm;j>=0;j--)
                    if(j>=sum[i])
                        jia(g[j],g[j-sum[i]]);
            }
        h[0]=1;
        for(int i=1,smm=0;i<=n;i++)
            if(v[i]==-1)
            {
                smm=min(d0,smm+s[i]);
                for(int j=smm;j>=0;j--)
                    if(j>=s[i])
                        jia(h[j],h[j-s[i]]);
            }
        for(int i=1;i<=c0;i++)
            jia(g[i],g[i-1]);
        for(int i=1;i<=d0;i++)
            jia(h[i],h[i-1]);
        for(int i=0;i<=c0;i++)
            if(ss-i-c1-1<c0-i)
                for(int j=0;j<=d0;j++)
                    if(ss-j-d1-1<d0-j)
                        jia(ans,1ll*f[i][j]*(g[c0-i]-(ss-i-c1-1>=0?g[ss-i-c1-1]:0)+mod)%mod*(h[d0-j]-(ss-j-d1-1>=0?h[ss-j-d1-1]:0)+mod)%mod);
        printf("%d\n",(ans+mod)%mod);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/10712364.html

时间: 2024-08-11 19:25:51

bzoj 5498: [2019省队联测]皮配【dp】的相关文章

bzoj 5496: [2019省队联测]字符串问题【SAM+拓扑】

有一个想法就是暴力建图,把每个A向有和他相连的B前缀的A,然后拓扑一下,这样的图是n^2的: 考虑优化建图,因为大部分数据结构都是处理后缀的,所以把串反过来,题目中要求的前缀B就变成了后缀B 建立SAM,发现在parent树中每个B能走到的A都在子树中,所以保留这个树结构,连边权为0的边: 然后在parent树上倍增找到每个AB串对应的点,因为SAM上每个对应不止一个串,所以找完之后把对应多个AB串的点拆成一条链 然后对于一对(x,y)的AB串关系,Ax对应的点向By对应的点连边权为A长度的边

bzoj 5499: [2019省队联测]春节十二响【堆】

首先看两条链怎么合并,贪心可得是从大到小取max,多条链同理 所以dfs合并子树的大根堆即可,注意为了保证复杂度,合并的时候要合并到最长链上,证明见长链剖分 #include<iostream> #include<cstdio> #include<queue> using namespace std; const int N=200005; int n,a[N],h[N],cnt,id[N],tot,p[N]; long long ans; priority_queue

bzoj 5249 [2018多省省队联测] IIIDX

bzoj 5249 [2018多省省队联测] IIIDX Link Solution 首先想到贪心,直接按照从大到小的顺序在后序遍历上一个个填 但是这样会有大问题,就是有相同的数的时候,会使答案不优 比如考虑 \((1, 2)(1, 3)(2, 4)\) 这样一棵树,并且点权是 \({1,1,1,2}\) 那么直接贪心会使得答案为 \(v_1=1,v_2=1,v_3=1,v_4=2\),但是实际上最优解为 \(v_1=1,v_2=1,v_3=2,v_4=1\) 问题出在我们先考虑 \(v_2\)

[bzoj 1911][Apio 2010]特别行动队(斜率优化DP)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1911 分析: 首先可以的到裸的方程f[i]=max{f[j]+a*(Si-Sj)^2+b*(Si-Sj)+c} 0<j<i 简化一下方程,我们知道对于一次项,最后结果肯定是b*Sn 所以可以写成f[i]=max{f[j]+a*(Si-Sj)^2+c} 0<j<i 我们不妨设0<x<y<i,且x比y优 即f[x]+a*(Si-Sx)^2+c>f[y]+a*

BZOJ 2734 集合选数(状态压缩DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2734 题意:给出一个由1到n的数字组成的集合.定义合法子集为若x在子集中则2x.3x均不能在子集中.求有多少个合法的子集. 思路: 1   3    9 2   6    12 4   12   36 对于上面的矩阵,我们发现就等价于不选相邻数字的方案数.因此枚举每个还没有用到的数字,建立以该数字为左上角的矩阵.接着就是状态压缩DP. int a[N][N]; i64 f[2][1<<

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=

[BZOJ 3530] [Sdoi2014] 数数 【AC自动机+DP】

题目链接:BZOJ - 3530 题目分析 明显是 AC自动机+DP,外加数位统计. WZY 神犇出的良心省选题,然而去年我太弱..比现在还要弱得多.. 其实现在做这道题,我自己也没想出完整解法.. 就想出了个 O(l^3) 的做法: 完全按照数位统计的思想来,先统计长度不足 len 的数字的合法种类数,这个枚举开头,然后 AC 自动机 DP 一下,用 f[i][j] 表示到了第 i 位,在第 j 个节点上的合法数字个数.这样是 O(L^2). 然后长度等于 n 的部分,就按照数位统计,一位位向

BZOJ 1079: [SCOI2008]着色方案(巧妙的dp)

BZOJ 1079: [SCOI2008]着色方案(巧妙的dp) 题意:有\(n\)个木块排成一行,从左到右依次编号为\(1\)~\(n\).你有\(k\)种颜色的油漆,其中第\(i\)种颜色的油漆足够涂\(c_i\)个木块.所有油漆刚好足够涂满所有木块,即\(\sum\limits _{i=1}^{k}c_i=n\).统计任意两个相邻木块颜色不同的着色方案.(\(1 \le k \le 15\) ,\(1\le c_i \le 5\)) 题解:特别巧妙的dp!一开始容易想到用\({c_i}^k

【刷题】BZOJ 5249 [2018多省省队联测]IIIDX

Description [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了.这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻.同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高. [题目描述] 这一天,Konano接到了一个任务,他需要给正在制作中的