「十二省联考 2019」皮配

题目大意

avatar

题解

首先,可以想到一个\(O(n*m^2)\)的暴力(虽然我考试时没想到)。将学校按所属城市排序,记\(f[x][ty][i][j]\)为前x个学校,前一个学校选择了ty阵营,此时蓝有i个人,鸭派有j个人的方案数。如果\(x\)\((x-1)\)在同一个城市,那么必须选同阵营。
然后膜题解分析\(k=0\)的情况,发现选阵营和选派系可以分开算!哇smg。。。背包一下然后直接把两次算的方案数乘起来就行了。。
\(k>0\) 的时候,发现有一些学校不能在选某派的同时加入某阵营。但是其他学校选阵营和选派系似乎还是可以分开算。。。于是把未强制的学校按照上一种方法背包一下派系;没有学校被强制的城市也背包一下阵营。。其实就是前两种算法合起来。。。对于有被强制的城市,注意在选择一个阵营以后要把这个城市所有学校全部加入该阵营。。
具体实现见代码。。。
时间复杂度\(O(T*(c*m+n*m+k^2*m\)*10\())\)

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int Q=2555;
int F[Q],G[Q];
int f[2][2][Q][Q];
struct dt{
    int bel,num,dis;
}p1[Q],p2[Q];
bool operator<(dt a,dt b)
{return a.bel<b.bel;}
int bl[Q],val[Q],ops[Q],gg[Q];
const int MOD=998244353;
inline void Add(int &a,int b)
{a+=b;a>=MOD?a-=MOD:1;}
inline int sub(int a,int b)
{a-=b;return a<0?a+MOD:a;}
inline int mul(int a,int b)
{return 1LL*a*b%MOD;}
int rev[Q];
int GG(int l,int r)
{return l>r?0:(l?sub(G[r],G[l-1]):G[r]);}
int GF(int l,int r)
{return l>r?0:(l?sub(F[r],F[l-1]):F[r]);}
void solve()
{
    int c0,c1,d0,d1;
    int tp1=0,tp2=0;
    int n,c;
    int sum=0;
    scanf("%d%d",&n,&c);
    scanf("%d%d%d%d",&c0,&c1,&d0,&d1);
    for(int i=1;i<=c;i++)gg[i]=0;
    for(int i=1;i<=n;i++){
        ops[i]=-1;
        scanf("%d%d",&bl[i],&val[i]);
        gg[bl[i]]+=val[i];
        sum+=val[i];
    }
    int sss;
    for(scanf("%d",&sss);sss;--sss){
        int x;
        scanf("%d",&x);
        scanf("%d",&ops[x]);
    }
    for(int i=1;i<=n;i++)
        (ops[i]>=0?p1[++tp1]:p2[++tp2])=(dt){bl[i],val[i],ops[i]};
    sort(p1+1,p1+tp1+1),sort(p2+1,p2+tp2+1);
    for(int i=1;i<=tp1;i++){
        if(gg[p1[i].bel]<0)rev[i]=0;
        else rev[i]=gg[p1[i].bel],gg[p1[i].bel]=-1;
    }
//
    G[0]=1;
    for(int i=1;i<=c0;i++)G[i]=0;
    for(int i=1;i<=c;i++)
        if(gg[i]>0)
            for(int j=c0;j>=gg[i];--j)
                Add(G[j],G[j-gg[i]]);
    
    for(int i=1;i<=c0;i++)
        Add(G[i],G[i-1]);
//
    F[0]=1;
    for(int i=1;i<=d0;i++)F[i]=0;
    for(int i=1;i<=tp2;i++){
        int Num=p2[i].num;
        for(int j=d0;j>=Num;--j)
            Add(F[j],F[j-Num]);
    }
    for(int i=1;i<=d0;i++)
        Add(F[i],F[i-1]);
    //
    for(int ty=0;ty<2;ty++)
        for(int i=0;i<=c0;i++)
            f[0][ty][i][0]=0;
    f[0][0][0][0]=1;
    int mxx=0;
    int now=0,lst=1;
    for(int i=1;i<=tp1;i++){
        now^=1,lst^=1;
        int Num=p1[i].num,oo=p1[i].dis,Dif=rev[i],lv=mxx;
        mxx+=Num;
        for(int ty=0;ty<2;ty++)
            for(int i=0;i<=c0;i++)
                for(int j=0;j<=mxx;j++)
                    f[now][ty][i][j]=0;
        for(int ty=0;ty<2;ty++){
            int mus=-1;
            if(i>1&&p1[i].bel==p1[i-1].bel)mus=ty;
            for(int i=c0;i>=0;--i)
                for(int j=mxx;j>=0;--j){
                    if(mus!=1){
                        if(oo!=0&&i>=Dif&&j>=Num&&j-Num<=lv)Add(f[now][0][i][j],f[lst][ty][i-Dif][j-Num]);
                        if(oo!=1&&i>=Dif&&j<=lv)Add(f[now][0][i][j],f[lst][ty][i-Dif][j]);
                    }
                    if(mus!=0){
                        if(oo!=2&&j>=Num&&j-Num<=lv)Add(f[now][1][i][j],f[lst][ty][i][j-Num]);
                        if(oo!=3&&j<=lv)Add(f[now][1][i][j],f[lst][ty][i][j]);
                    }
            }
        }
    }
    int als=0;
    for(int ty=0;ty<2;ty++)
        for(int i=0;i<=c0;i++)
            for(int j=0;j<=mxx;j++){
                int val=f[now][ty][i][j];
                if(!val)continue;
                int cl=max(0,sum-c1-i),cr=c0-i;
                int nl=max(0,sum-d1-j),nr=d0-j;
                Add(als,mul(val,mul(GG(cl,cr),GF(nl,nr))));
            }
    printf("%d\n",als);
}
int main()
{
    int t;
    for(scanf("%d",&t);t;--t)
        solve();
    return 0;
}
posted @ 2019-04-08 17:56 蒟蒻小果冻 阅读(...) 评论(...) 编辑 收藏