解:观察一波部分分。

首先小数据直接暴力4n,然后考虑背包。设f[i][a][b][c]表示前i个学校中前三位导师分别有多少人,第四位导师可以直接推出来。

然后暴力枚举每一个人放在哪进行背包。

进一步发现,因为限制条件全是跟行列有关的,所以我们设f[i][a][b]表示前i个学校,第一列和第一行分别有多少人。这样也能够控制满足那4个限制。

于是我们有了一个m2n的50分DP。

然后发现k = 0的时候行列独立。于是对行列分别DP,然后乘起来,这样有70分。

 #include <bits/stdc++.h>

 const int N = , MO = ;

 int C0, D0, C1, D1, n, c, belong[N], s[N], sum[N], K, d[N], lm[];
int f[][][N][N];
std::vector<int> v[N]; inline void clear() {
for(int i = ; i <= n; i++) {
v[i].clear();
sum[i] = ;
d[i] = ;
}
memset(f, , sizeof(f));
return;
} inline void solve() {
scanf("%d%d%d%d%d%d", &n, &c, &C0, &C1, &D0, &D1);
lm[] = std::min(C0, D0);
lm[] = std::min(C0, D1);
lm[] = std::min(C1, D0);
lm[] = std::min(C1, D1);
for(int i = ; i <= n; i++) {
scanf("%d%d", &belong[i], &s[i]);
}
scanf("%d", &K);
for(int i = , x; i <= K; i++) {
scanf("%d", &x);
scanf("%d", &d[x]);
d[x]++;
} for(int i = ; i <= n; i++) {
v[belong[i]].push_back(i);
sum[belong[i]] += s[i];
} if(n > ) { #define h0 f[0][0][0]
#define h1 f[1][1][1] h0[] = h1[] = ;
int tot = ;
for(int i = ; i <= c; i++) {
int limit = v[i].size();
if(!limit) continue;
int Sum = ;
for(int j = ; j < limit; j++) {
int t = v[i][j]; /// city i school t
Sum += s[t];
for(int V = D0; V >= s[t]; V--) {
(h0[V] += h0[V - s[t]]) %= MO;
}
}
for(int V = C0; V >= Sum; V--) {
(h1[V] += h1[V - Sum]) %= MO;
}
tot += Sum;
} int ans = ;
for(int V1 = std::max(, tot - D1); V1 <= D0; V1++) {
for(int V2 = std::max(, tot - C1); V2 <= C0; V2++) {
(ans += 1ll * h0[V1] * h1[V2] % MO) %= MO;
}
}
printf("%d\n", ans); #undef h0
#undef h1 return;
} int Sum = , FLAG = ;
f[][][][] = f[][][][] = ;
for(int i = ; i <= c; i++) {
int limit = v[i].size();
if(!limit) continue;
for(int j = ; j < limit; j++) {
int t = v[i][j]; /// city i school t
Sum += s[t];
FLAG ^= ;
int (*f0)[N] = f[FLAG][], (*f1)[N] = f[FLAG][], (*g0)[N] = f[FLAG ^ ][], (*g1)[N] = f[FLAG ^ ][];
for(int V1 = ; V1 <= D0 && V1 <= Sum; V1++) {
for(int V2 = ; V2 <= C0 && V2 <= Sum; V2++) {
if(d[t] != && V1 >= s[t] && V2 >= s[t]) ///
f0[V1][V2] = g0[V1 - s[t]][V2 - s[t]];
else
f0[V1][V2] = ;
if(d[t] != && V2 >= s[t] && Sum - V1 >= s[t]) ///
(f0[V1][V2] += g0[V1][V2 - s[t]]) %= MO;
if(d[t] != && V1 >= s[t] && Sum - V2 >= s[t]) ///
f1[V1][V2] = g1[V1 - s[t]][V2];
else
f1[V1][V2] = ;
if(d[t] != && Sum - V1 >= s[t] && Sum - V2 >= s[t]) ///
(f1[V1][V2] += g1[V1][V2]) %= MO;
}
}
}
///
for(int V1 = ; V1 <= Sum; V1++) {
for(int V2 = ; V2 <= Sum; V2++) {
(f[FLAG][][V1][V2] += f[FLAG][][V1][V2]) %= MO;
f[FLAG][][V1][V2] = f[FLAG][][V1][V2];
}
}
} int ans = ;
for(int V1 = std::max(, Sum - D1); V1 <= D0; V1++) {
for(int V2 = std::max(, Sum - C1); V2 <= C0; V2++) {
(ans += f[FLAG][][V1][V2]) %= MO;
}
}
printf("%d\n", ans);
return;
} int main() { freopen("in.in", "r", stdin);
freopen("right.out", "w", stdout); int T;
scanf("%d", &T);
while(T--) {
solve();
clear();
}
return ;
}

70分代码

正解:

把上面两种部分分结合起来。发现无标号的行列,有标号这三个东西全都互相独立。

具体来说,把城市和学校都分成两类,有标记和没标记。如果一个学校有标记那么它城市也有标记。然后枚举每个无标记城市,对上下做DP。然后枚举每个无标记学校,对左右做DP。

然后对有标记的进行50分DP。这里有一个坑点......当你一个城市总人数大于C0但是受限制人数小于C0的时候,你可能会多算一种方案,即受限制的学校在上面,但是别的却在下面。

出现这个问题的原因是∑school != city,因为有些无标记学校已经拿出去算了。

所以我们要想办法把一个城市的有无标记的学校都限制在同一横条。具体来说......之前DP的时候,我们是每加一个学校就同时处理行列限制,而现在是先分成上下两部分,然后分别转移每个学校的左右。最后转移这个城市的上下,相当于为那些无标记的学校预留了位置。

记得把上下界开松一点......还有就是对城市DP的时候跳过空城市。

最后统计答案的时候,对于有标记的每一个状态,考虑把无标记的怎么塞进去。有个上下界的限制,在这个上下界之中的每个方法都是合法的,于是我们对于无标记的DP数组做前缀和,然后相乘就行了...

 #include <bits/stdc++.h>

 const int N = , MO = ;

 int C0, D0, C1, D1, n, c, belong[N], s[N], K, d[N], h0[N], h1[N], sum[N];
int f[][][N][N], tot;
bool vis[N];
std::vector<int> v[N]; inline void clear() {
for(int i = ; i <= n || i <= c; i++) {
v[i].clear();
sum[i] = d[i] = vis[i] = ;
}
tot = ;
memset(f, , sizeof(f));
memset(h0, , sizeof(h0));
memset(h1, , sizeof(h1));
return;
} inline int cal(int V1, int V2) {
int l1 = std::max(, tot - D1 - V1), r1 = D0 - V1;
int l2 = std::max(, tot - C1 - V2), r2 = C0 - V2;
if(l1 > r1 || l2 > r2) return ;
int temp1 = (h0[r1] - (l1 ? h0[l1 - ] : ) + MO) % MO, temp2 = (h1[r2] - (l2 ? h1[l2 - ] : ) + MO) % MO;
return 1ll * temp1 * temp2 % MO;
} inline void solve() {
scanf("%d%d%d%d%d%d", &n, &c, &C0, &C1, &D0, &D1);
for(int i = ; i <= n; i++) {
scanf("%d%d", &belong[i], &s[i]);
v[belong[i]].push_back(i);
sum[belong[i]] += s[i];
tot += s[i];
}
scanf("%d", &K);
for(int i = , x; i <= K; i++) {
scanf("%d", &x);
scanf("%d", &d[x]);
d[x]++;
vis[belong[x]] = ;
} int LM = std::max(tot, std::max(C0 + C1, D0 + D1));
/// first no limit DP
h0[] = h1[] = ;
int Sum = ;
for(int i = ; i <= n; i++) {
if(d[i]) continue;
Sum += s[i];
for(int V = Sum; V >= s[i]; V--) {
(h0[V] += h0[V - s[i]]) %= MO;
}
}
for(int i = ; i <= LM; i++) {
(h0[i] += h0[i - ]) %= MO;
} Sum = ;
for(int i = ; i <= c; i++) {
if(vis[i] || !sum[i]) continue;
Sum += sum[i];
for(int V = Sum; V >= sum[i]; V--) {
(h1[V] += h1[V - sum[i]]) %= MO;
}
}
for(int i = ; i <= LM; i++) {
(h1[i] += h1[i - ]) %= MO;
} /// second limit DP
Sum = ;
int FLAG = , Sum2 = ;
f[][][][] = f[][][][] = ;
for(int i = ; i <= c; i++) {
int limit = v[i].size();
if(!limit || !vis[i]) continue;
Sum2 += sum[i];
for(int j = ; j < limit; j++) {
int t = v[i][j]; /// city i school t
if(!d[t]) continue;
Sum += s[t];
FLAG ^= ;
int (*f0)[N] = f[FLAG][], (*f1)[N] = f[FLAG][], (*g0)[N] = f[FLAG ^ ][], (*g1)[N] = f[FLAG ^ ][];
for(int V1 = ; V1 <= D0 && V1 <= Sum; V1++) {
for(int V2 = ; V2 <= C0 && V2 <= Sum2; V2++) {
if(d[t] != && V1 >= s[t]) ///
f0[V1][V2] = g0[V1 - s[t]][V2];
else
f0[V1][V2] = ;
if(d[t] != && Sum - V1 >= s[t]) ///
(f0[V1][V2] += g0[V1][V2]) %= MO; if(d[t] != && V1 >= s[t]) ///
f1[V1][V2] = g1[V1 - s[t]][V2];
else
f1[V1][V2] = ;
if(d[t] != && Sum - V1 >= s[t]) ///
(f1[V1][V2] += g1[V1][V2]) %= MO;
}
}
}
/// FLAG ^= ;
for(int V1 = ; V1 <= Sum && V1 <= D0; V1++) {
for(int V2 = ; V2 <= Sum2 && V2 <= C0; V2++) {
/// up
if(V2 >= sum[i])
f[FLAG][][V1][V2] = f[FLAG ^ ][][V1][V2 - sum[i]];
else
f[FLAG][][V1][V2] = ;
/// down
if(Sum2 - V2 >= sum[i])
(f[FLAG][][V1][V2] += f[FLAG ^ ][][V1][V2]) %= MO;
f[FLAG][][V1][V2] = f[FLAG][][V1][V2];
}
}
} int ans = ;
for(int V1 = ; V1 <= D0; V1++) {
for(int V2 = ; V2 <= C0; V2++) {
(ans += 1ll * f[FLAG][][V1][V2] * cal(V1, V2) % MO) %= MO;
}
}
printf("%d\n", ans);
return;
} int main() { int T;
scanf("%d", &T);
while(T--) {
solve();
if(T) {
clear();
}
}
return ;
}

AC代码

这TM考场上谁能写出来啊....十个我也搞不倒......

洛谷P5289 皮配的更多相关文章

  1. 洛谷P5289 [十二省联考2019]皮配(01背包)

    啊啊啊边界判错了搞死我了QAQ 这题是一个想起来很休闲写起来很恶心的背包 对于\(k=0\)的情况,可以发现选阵营和选派系是独立的,对选城市选阵营和学校选派系分别跑一遍01背包就行了 对于\(k> ...

  2. 洛谷P4382 劈配

    不知道这个Zayid是谁... 题意: 有n个人,m个导师.每个导师能接纳bi个人,每个人对于这m个导师都有一个志愿档次. 优先满足靠前的人,问到最后每个人匹配的导师是他的第几志愿. 每个人又有一个限 ...

  3. 洛谷P4382 [八省联考2018]劈配(网络流,二分答案)

    洛谷题目传送门 说不定比官方sol里的某理论最优算法还优秀一点? 所以\(n,m\)说不定可以出到\(1000\)? 无所谓啦,反正是个得分题.Orz良心出题人,暴力有70分2333 思路分析 正解的 ...

  4. luogu P5289 [十二省联考2019]皮配 背包

    LINK:皮配 我承认是一道很难的题目. 不过对于这道题 部分分的提示显得尤为重要. 首先是 40分的暴力dp 很容易想 但是不容易写. 从40分可以发现我们只需要把蓝阵营和鸭派系的人数给存在起来就行 ...

  5. 【BZOJ5498】[十二省联考2019]皮配(动态规划)

    [BZOJ5498][十二省联考2019]皮配(动态规划) 题面 BZOJ 洛谷 题解 先考虑暴力\(dp\),设\(f[i][j][k]\)表示前\(i\)所学校,有\(j\)人在某个阵营,有\(k ...

  6. 【洛谷P1352】没有上司的舞会

    [洛谷P1352]没有上司的舞会 x舷售 锚」翅θ 但是 拙臃 蓄ⅶ榔 暄条熨卫 翘ヴ馇 表现无愧于雪月工作室的核心管理 爸惚扎掬 颇瓶 芟缆肝 貌痉了 洵┭笫装 嗝◇裴腋 褓劂埭 ...

  7. 洛谷P1991 无线通讯网

    P1991 无线通讯网 170通过 539提交 题目提供者洛谷OnlineJudge 标签图论 难度普及+/提高 提交该题 讨论 题解 记录 最新讨论 怎么又炸了 为啥一直40!求解! UKE:inv ...

  8. COCI2017-2018#3 Dojave || 洛谷P4443

    题目传送门............................................................................................... ...

  9. 【洛谷4933】大师(DP)

    题目: 洛谷4933 分析: (自己瞎yy的DP方程竟然1A了,写篇博客庆祝一下) (以及特斯拉电塔是向Red Alert致敬吗233) 这里只讨论公差不小于\(0\)的情况,小于\(0\)的情况进行 ...

随机推荐

  1. 珍藏版Chrome插件送给你们,不仅是程序员必备

    大家好,消失了几天我又满血复活归来了,最近这几天太忙了一直在加班工作,这不昨天又干到凌晨一点,今天早上七点就起来了,到现在还都没有休息,现在只剩半血了,不对应该说现在只能爬着走了,但是一想到几天没有更 ...

  2. Linux & Windows 环境下 Redis 安装与基本配置

    索引: 目录索引 参看代码 GitHub: redis.txt 一.Linux (DeepinOS) 环境 .安装Redis服务 sudo apt-get install redis-server . ...

  3. mssql sqlserver 索引专题

    摘要: 下文将详细讲述sql server 索引的相关知识,如下所示: 实验环境: sql server 2008 R2 sqlserver索引简介: mssql sqlsever 索引分类简介 ms ...

  4. centos7查看可登陆用户

    一.命令 cat /etc/passwd | grep -v /sbin/nologin | cut -d : -f 1 cat /etc/passwd | grep   /bin/bash | cu ...

  5. momentjs的使用

    一.脚本引用 <script src="~/Scripts/moment.js"></script> <script src="~/Scri ...

  6. netcat的简单使用(一)

    简单写一下netcat这个强悍的工具,主要是怕自己忘了 功能大致这些个,有遗漏的欢迎私信补充 1.侦听模式/传输模式 2.telnet/获取banner信息 3.传输文本信息 4.传输文件/目录 5. ...

  7. DB2 因版本问题 Reorg 出错 解决办法

    call Sysproc.admin_cmd('REORG TABLE MY_TABLE_NAME');

  8. kubernetes-整体概述和架构

    1.Kubernetes是什么 Kubernetes是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务.通过Kubernetes能够进行应用的自动化部署和扩缩容.在Kubernetes中,会将组 ...

  9. koa 路由配置

    Koa 路由 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET.POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问. 通俗的讲:路由就是根据不 ...

  10. Topshelf:一款非常好用的 Windows 服务开发框架

    背景 多数系统都会涉及到“后台服务”的开发,一般是为了调度一些自动执行的任务或从队列中消费一些消息,开发 windows service 有一点不爽的是:调试麻烦,当然你还需要知道 windows s ...