luogu P5289 [十二省联考2019]皮配
首先考虑一个正常的dp,设\(f_{i,j,k}\)为前\(i\)个学校,\(j\)人在\(\color{#0000FF}{蓝阵营}\),\(k\)人在\(\color{#654321}{吔}\)派系的方案,转移枚举选哪个导师就好了,注意一个城市要选同一阵营,所以可以多开一维\(0/1\)表示当前城市在哪个阵营
\(k=0\)的情况,可以发现选\(\color{#654321}{吔}\)派系的\(Yazid\)和\(Zayid\)都会增加\(\color{#654321}{吔}\)派系人数,另一个派系亦然,所以选阵营和选派系是相对独立的,所以可以dp出\(g_{i,j}\)为前\(i\)个城市\(j\)人在\(\color{#0000FF}{蓝阵营}\)的方案,以及\(h_{i,k}\)为前\(i\)个学校,\(k\)人在\(\color{#654321}{吔}\)派系的方案,然后两者之和的乘积就是答案
\(k\le 30\)的情况,一个显然的想法是把所有有限制的城市单独拿出来做\(f\)数组的dp,然后其他的做\(g\)和\(h\)的dp,然后方案拼起来.至于拼起来,就是枚举\(f\)的\(j\)和\(k\),然后其他城市的维度就会限制在一个范围内,就是区间和乘起来,可以前缀和求.
只不过可能这些城市的学校贼多,,,不过那些城市的没限制学校还是可以乱选阵营的,所以可以把选阵营的贡献算到\(h\)里.具体说就是把\(f_{i,j,k}\)改为前\(i\)个限制的城市的学校,\(j\)人在\(\color{#0000FF}{蓝阵营}\),限制学校中\(k\)人在\(\color{#654321}{吔}\)派系的方案,然后转移是碰到无限制学校就转移\(h\)
// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db double
using namespace std;
const int N=2500+10,M=300+10,mod=998244353;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
void ad(int &x,int y){x+=y,x-=x>=mod?mod:0;}
int f[2][2][N][M],g[2][N],h[2][N];
int n,c,c0,c1,d0,d1,kk,m;
bool ht[N];
struct sch
{
int b,s,m;
bool operator < (const sch &bb) const {return ht[b]!=ht[bb.b]?ht[b]<ht[bb.b]:b<bb.b;}
}a[N];
int main()
{
int T=rd();
while(T--)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(h,0,sizeof(h));
n=rd(),c=rd();
for(int i=1;i<=c;++i) ht[i]=0;
c0=rd(),c1=rd(),d0=rd(),d1=rd();
for(int i=1;i<=n;++i) a[i].b=rd(),a[i].s=rd(),a[i].m=-1;
kk=rd();
for(int i=1;i<=kk;++i)
{
int x=rd(),y=rd();
ht[a[x].b]=1,a[x].m=y;
}
sort(a+1,a+n+1);
int m=n;
while(m&&ht[a[m].b]) --m;
int nw=1,la=0;
g[la][0]=1;
for(int i=1,sm=0,sb=0;i<=m;++i)
{
sm+=a[i].s,sb+=a[i].s;
if(i<m&&a[i].b==a[i+1].b) continue;
for(int j=max(sm-sb-c1,0);j<=sm-sb&&j<=c0;++j)
{
if(!g[la][j]) continue;
if(j+sb<=c0) ad(g[nw][j+sb],g[la][j]);
if(sm-j<=c1) ad(g[nw][j],g[la][j]);
}
memset(g[la],0,sizeof(int)*(c0+3));
sb=0;
nw^=1,la^=1;
}
int lg=la;
nw=1,la=0;
h[la][0]=1;
for(int i=1,sm=0;i<=m;++i)
{
sm+=a[i].s;
for(int j=max(sm-a[i].s-d1,0);j<=sm-a[i].s&&j<=d0;++j)
{
if(!h[la][j]) continue;
if(j+a[i].s<=d0) ad(h[nw][j+a[i].s],h[la][j]);
if(sm-j<=d1) ad(h[nw][j],h[la][j]);
}
memset(h[la],0,sizeof(int)*(d0+3));
nw^=1,la^=1;
}
int lh=la,nh=nw,sk=0,sum=0,ans=0;
for(int i=1;i<=m;++i) sum+=a[i].s;
nw=1,la=0;
f[la][0][0][0]=1;
for(int i=m+1,sm=0;i<=n;++i)
{
bool fg=0;
sm+=a[i].s;
if(i==m+1||a[i].b!=a[i-1].b)
{
for(int j=0;j<=c0;++j)
for(int k=0;k<=sk;++k)
f[la][0][j][k]=f[la][1][j][k]=(f[la][0][j][k]+f[la][1][j][k])%mod;
}
if(~a[i].m)
{
sk+=a[i].s;
for(int j=max(sm-a[i].s-c1,0);j<=sm-a[i].s&&j<=c0;++j)
for(int k=0;k<=sk;++k)
{
if(f[la][0][j][k])
{
if(a[i].m!=0&&j+a[i].s<=c0) ad(f[nw][0][j+a[i].s][k+a[i].s],f[la][0][j][k]);
if(a[i].m!=1&&j+a[i].s<=c0) ad(f[nw][0][j+a[i].s][k],f[la][0][j][k]);
}
if(f[la][1][j][k])
{
if(a[i].m!=2&&sm-j<=c1) ad(f[nw][1][j][k+a[i].s],f[la][1][j][k]);
if(a[i].m!=3&&sm-j<=c1) ad(f[nw][1][j][k],f[la][1][j][k]);
}
}
}
else
{
for(int j=max(sm-a[i].s-c1,0);j<=sm-a[i].s&&j<=c0;++j)
for(int k=0;k<=sk;++k)
{
if(f[la][0][j][k]&&j+a[i].s<=c0) ad(f[nw][0][j+a[i].s][k],f[la][0][j][k]);
if(f[la][1][j][k]&&sm-j<=c1) ad(f[nw][1][j][k],f[la][1][j][k]);
}
for(int j=max(sum+sm-sk-a[i].s-d1,0);j<=sum+sm-sk&&j<=d0;++j)
{
if(!h[lh][j]) continue;
if(j+a[i].s<=d0) ad(h[nh][j+a[i].s],h[lh][j]);
if(sum+sm-sk-j<=d1) ad(h[nh][j],h[lh][j]);
}
fg=1;
}
for(int j=0;j<=c0;++j)
for(int k=0;k<=sk;++k)
f[la][0][j][k]=f[la][1][j][k]=0;
nw^=1,la^=1;
if(fg)
{
memset(h[lh],0,sizeof(int)*(d0+3));
nh^=1,lh^=1;
}
}
for(int j=1;j<=c0;++j) ad(g[lg][j],g[lg][j-1]);
for(int j=1;j<=d0;++j) ad(h[lh][j],h[lh][j-1]);
for(int i=m+1;i<=n;++i) sum+=a[i].s;
for(int j=0;j<=c0;++j)
for(int k=0;k<=sk;++k)
{
if((f[la][0][j][k]+f[la][1][j][k])%mod==0) continue;
int l1=max(sum-j-c1,0),r1=min(sum,c0)-j,l2=max(sum-k-d1,0),r2=min(sum,d0)-k;
if(l1<=r1&&l2<=r2)
{
int gg=(g[lg][r1]-(l1?g[lg][l1-1]:0)+mod)%mod,hh=(h[lh][r2]-(l2?h[lh][l2-1]:0)+mod)%mod;
ans=(ans+1ll*(f[la][0][j][k]+f[la][1][j][k])%mod*gg%mod*hh%mod)%mod;
}
}
printf("%d\n",ans);
}
return 0;
}
luogu P5289 [十二省联考2019]皮配的更多相关文章
- luogu P5289 [十二省联考2019]皮配 背包
LINK:皮配 我承认是一道很难的题目. 不过对于这道题 部分分的提示显得尤为重要. 首先是 40分的暴力dp 很容易想 但是不容易写. 从40分可以发现我们只需要把蓝阵营和鸭派系的人数给存在起来就行 ...
- 洛谷P5289 [十二省联考2019]皮配(01背包)
啊啊啊边界判错了搞死我了QAQ 这题是一个想起来很休闲写起来很恶心的背包 对于\(k=0\)的情况,可以发现选阵营和选派系是独立的,对选城市选阵营和学校选派系分别跑一遍01背包就行了 对于\(k> ...
- 【BZOJ5498】[十二省联考2019]皮配(动态规划)
[BZOJ5498][十二省联考2019]皮配(动态规划) 题面 BZOJ 洛谷 题解 先考虑暴力\(dp\),设\(f[i][j][k]\)表示前\(i\)所学校,有\(j\)人在某个阵营,有\(k ...
- Luogu5289 十二省联考2019皮配(动态规划)
将选择导师看成先选阵营再选派系,这样有显然的O(nm2)暴力,即按城市排序后,设f[i][j][k]为前i个学校中第一个阵营有j人第一个派系有k人的方案数,暴力背包. 对于k=0,可以发现选阵营和选派 ...
- 【LuoguP5289】[十二省联考2019] 皮配
题目链接 题目描述 略 Sol 一道背包问题 首先暴力做法设 \(dp[i][j][k]\) 表示前 \(i\) 个城市的学校被分到第一阵营 \(j\) 人 第一门派 \(k\) 人的方案数. 中间一 ...
- Luogu P5285 [十二省联考2019]骗分过样例
Preface ZJOI一轮被麻将劝退的老年选手看到这题就两眼放光,省选也有乱搞题? 然后狂肝了3~4天终于打完了,期间还补了一堆姿势 由于我压缩技术比较菜,所以用的都是非打表算法,所以一共写了5K- ...
- luogu P5291 [十二省联考2019]希望
luogu loj 无论最终结果将人类历史导向何处 \(\quad\)我们选择 \(\quad\quad\)\(\large{希望}\) 诶我跟你讲,这题超修咸的 下面称离连通块内每个点距离不超过\( ...
- Luogu P5290 [十二省联考2019]春节十二响
这题是最近看到的今年省选题中最良心的一道了吧 看题+想题+写题都可以在0.5h内解决,送分含义明显啊 首先理解了题意后我们很快就能发现两个点如果要被分在一段那么必须在它们的祖先处合并 首先我们考虑下二 ...
- Luogu P5284 [十二省联考2019]字符串问题
好难写的字符串+数据结构问题,写+调了一下午的说 首先理解题意后我们对问题进行转化,对于每个字符串我们用一个点来代表它们,其中\(A\)类串的点权为它们的长度,\(B\)类串的权值为\(0\) 这样我 ...
随机推荐
- HBase Rowkey 设计指南
为什么Rowkey这么重要 RowKey 到底是什么 我们常说看一张 HBase 表设计的好不好,就看它的 RowKey 设计的好不好.可见 RowKey 在 HBase 中的地位.那么 RowKey ...
- loadrunner迭代和并发的区别
转载: ZEE的回答: 用比喻的方式来回一下: 四车道的马路,如果只有四辆车并排走过就是并发: 如果四辆车排成一纵队走过就是迭代: 如果有100辆车排成25行依次走过就是并发加迭代. 在以上说法中,只 ...
- JDK内置工具使用(jps、jstack、jmap、jstat)
一.JPS 1.jps -lvm:用于查看当前机器上已装载的jvm 二.jstackjstack命令主要用来查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁) 1.jstack -l pid ...
- 前端——JavaScript
何谓JavaScript?它与Java有什么关系? JavaScript与HTML.CSS组合使用应用于前端开发,JavaScript是一门独立的语言,浏览器内置了JS的解释器.它除了和Java名字长 ...
- 初学Python——协程
进程.线程和协程区分 我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译为协同的例程,一般我们都简称为协程. 在linux系统中,线程就是轻量级的进程,而我们 ...
- 可视化工具Grafana:简介及安装
随着业务的越发复杂,对软件系统的要求越来越高,这意味着我们需要随时掌控系统的运行情况.因此,对系统的实时监控以及可视化展示,就成了基础架构的必须能力. 这篇博客,介绍下开源的可视化套件grafana的 ...
- Golang 入门系列(四)如何理解interface接口
前面讲了很多Go 语言的基础知识,包括go环境的安装,go语言的语法等,感兴趣的朋友,可以先看看之前的文章.https://www.cnblogs.com/zhangweizhong/category ...
- Mysql完整约束性
一.介绍 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性主要分为: PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录 FOREIGN KEY ...
- supervisor 守护者进程配置小记
安装 Supervisor 联网状态下,官方推荐首选安装方法是使用easy_install,它是setuptools(Python 包管理工具)的一个功能.所以先执行如下命令安装 setuptools ...
- 转 vue实现双向数据绑定之原理及实现篇
转自:https://www.cnblogs.com/canfoo/p/6891868.html vue的双向绑定原理及实现 前言 先上个成果图来吸引各位: 代码: ...