十二省联考2019 皮配

十二省联考2019 皮配


可以想到一个暴力就是f[i][j=0/1][k][l]表示dp了i个学校,第i个学校选择的阵营(1蓝,0红),选了蓝阵营的人数为k,选了鸭派系的人数为l。

先对所有学校按城市排序,那么就可以直接dp了。

k=0有一个想法就是没有多余的限制时,每个城市的阵营和学校的派系互不影响,可以设f[i][j]表示dp了i个城市,选了蓝阵营的人数为j;g[i][j]表示dp了i个派系,选了鸭派系的人数为l。

现在讲正解。

一个城市有限制当且仅当有一个学校属于这个城市有限制。

那么没有限制的城市可以按照k=0方法做,最后和正解方法合并,有限制的城市做一遍暴力。

但是可能有限制的城市中的学校很多,那些在有限制的城市中没有限制的学校就会被dp到很不爽。

按照k=0继续想,这些被多余计算的学校的派系和它们城市的阵营还是互不影响。

那么可以单独dp这些学校的派系,只要对于有限制的学校做暴力就行了。这个对派系的单独dp可以和上面其他没有限制的城市一起做。

具体实现,设

  • f[i][j][k]是前i个有限制的城市,所有学校中选蓝色阵营有j人,有限制的学校中鸭派系有k人的方案数
  • g[i][j]是前i个没有限制的城市,蓝色阵营有j人的方案数
  • h[i][j]是前i个没有限制的学校,鸭派系有j人的方案数

这些可以用滚动数组滚掉第一维i,就开的下了。

求f的时候先枚举有限制的城市的阵营,再dp这个城市里有限制的学校的派系。

剩下两个就是裸的01背包。

时间看似很大,但是如果每次dp只dp到上界就快的飞起了。

注意memcpy也要只memcpy到上界否则会慢的飞起

#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 998244353
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int b[1010],s[1010];
int f[2503][2503],_f[2503][2503],__f[2503][2503],g[2503],h[2503];
// f[i][j][k] 前i个有限制的城市,所有学校中选蓝色阵营有j人,有限制的学校中鸭派系有k人,滚掉i
// g[i][j] 前i个没有限制的城市,蓝色阵营有j人
// h[i][j] 前i个没有限制的学校,鸭派系有j人
int ki[1010],kp[1010],ban[1010];//学校的限制
int city[31],m;//有限制的城市数量
std::vector<int>school[1010];//有限制的城市中有限制的学校
int sum[1010],_sum[1010];//城市的学校人数/城市有限制的学校人数
il vd add(int&a,int b){a+=b;if(a>=mod)a-=mod;}
il int get_add(int a,int b){a+=b;if(a>=mod)a-=mod;return a;}
il vd memcpy(int f[2503][2503],int g[2503][2503],int sum1,int sum2){
	for(int i=0;i<=sum1;++i)
		for(int j=0;j<=sum2;++j)
			f[i][j]=g[i][j];
}
int main(){
#ifdef XZZSB
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    int yyborz=gi();
    while(yyborz--){
        int n=gi(),c=gi();
        for(int i=1;i<=c;++i)sum[i]=_sum[i]=0;
        for(int i=1;i<=c;++i)school[i].clear();
        int C0=gi(),C1=gi(),D0=gi(),D1=gi(),Sum=0;//Sum 所有人数之和
        for(int i=1;i<=n;++i)b[i]=gi(),s[i]=gi(),Sum+=s[i];
        int k=gi();m=0;
        for(int i=1;i<=n;++i)ban[i]=-1;
        for(int i=1;i<=k;++i){
            ki[i]=gi(),kp[i]=gi();
            city[++m]=b[ki[i]],school[b[ki[i]]].push_back(ki[i]),ban[ki[i]]=kp[i];
        }
        std::sort(city+1,city+m+1);m=std::unique(city+1,city+m+1)-city-1;
        for(int i=1;i<=n;++i)sum[b[i]]+=s[i];
        for(int i=1;i<=n;++i)if(~ban[i])_sum[b[i]]+=s[i];
        memset(f,0,sizeof f);memset(g,0,sizeof g);memset(h,0,sizeof h);
        f[0][0]=1;
		int sum1=0,sum2=0;
        for(int i=1;i<=m;++i){
			sum1=std::min(C0,sum1+sum[city[i]]);
			sum2=std::min(D0,sum2+_sum[city[i]]);
            for(int o=0;o<2;++o){//城市city[i]的阵营,o=0是蓝
                memcpy(_f,f,sum1,sum2);
                for(auto x:school[city[i]]){//计算城市中有限制的学校选择的派系
                    for(int j=sum1;~j;--j)
                        for(int k=sum2;~k;--k){
                            if(!o){
                                _f[j][k]=0;
                                if(ban[x]!=o*2+1&&j>=s[x])add(_f[j][k],_f[j-s[x]][k]);
                                if(ban[x]!=o*2&&k>=s[x]&&j>=s[x])add(_f[j][k],_f[j-s[x]][k-s[x]]);
                            }else{
                                if(ban[x]==o*2+1)_f[j][k]=0;
                                if(ban[x]!=o*2&&k>=s[x])add(_f[j][k],_f[j][k-s[x]]);
                            }
                        }
                }
                if(!o){
					int others=sum[city[i]]-_sum[city[i]];
                    for(int j=sum1;~j;--j)
                        for(int k=sum2;~k;--k)
                            if(j>=others)_f[j][k]=_f[j-others][k];
                            else _f[j][k]=0;
                }
                if(!o)memcpy(__f,_f,sum1,sum2);
                else{
                    for(int j=0;j<=sum1;++j)
                        for(int k=0;k<=sum2;++k)
                            f[j][k]=get_add(_f[j][k],__f[j][k]);
                }
            }
        }
        g[0]=1;
        for(int i=1,Sum=0;i<=c;++i){
            if(!school[i].empty())continue;
            if(!sum[i])continue;
			Sum=std::min(C0,sum[i]+Sum);
            for(int j=Sum;~j;--j)if(j>=sum[i])g[j]=(g[j]+g[j-sum[i]])%mod;
        }
        h[0]=1;
        for(int i=1,Sum=0;i<=n;++i){
            if(~ban[i])continue;
			Sum=std::min(D0,s[i]+Sum);
            for(int j=Sum;~j;--j)if(j>=s[i])h[j]=(h[j]+h[j-s[i]])%mod;
        }
        for(int i=1;i<=C0;++i)g[i]=(g[i]+g[i-1])%mod;
        for(int i=1;i<=D0;++i)h[i]=(h[i]+h[i-1])%mod;
        int ans=0;
        for(int i=0;i<=C0;++i)
            for(int j=0;j<=D0;++j){
                if(Sum-i-C1-1>=C0-i)continue;
                if(Sum-j-D1-1>=D0-j)continue;
                ans=(ans+1ll*f[i][j]*(g[C0-i]-(Sum-i-C1-1>=0?g[Sum-i-C1-1]:0)+mod)%mod*(h[D0-j]-(Sum-j-D1-1>=0?h[Sum-j-D1-1]:0)+mod))%mod;
            }
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2019-04-12 19:14  菜狗xzz  阅读(513)  评论(0编辑  收藏  举报