解:观察一波部分分。

首先小数据直接暴力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. Python第四天 流程控制 if else条件判断 for循环 while循环

    Python第四天   流程控制   if else条件判断   for循环 while循环 目录 Pycharm使用技巧(转载) Python第一天  安装  shell  文件 Python第二天 ...

  2. AngularJS学习之旅—AngularJS 服务(八)

    1.AngularJS 服务(Service) AngularJS 中你可以创建自己的服务,或使用内建服务.2.什么是服务? 在 AngularJS 中,服务是一个函数或对象,可在你的 Angular ...

  3. SQL语句中不同的连接JOIN

    为了从两个表中获取数据,我们有时会用JOIN将两个表连接起来.通常有以下几种连接方式: JOIN  or  INNER JOIN(内连接) : 这两个是相同的,要求两边表同时有对应的数据,返回行,任何 ...

  4. Python文件操作之把臂入林

    文件操作1.打开文件open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=Tru ...

  5. JPA之@Transient

    java 的transient关键字的作用是需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中. 使用示例: ...

  6. linux磁盘IO读写性能优化

    在LINUX系统中,如果有大量读请求,默认的请求队列或许应付不过来,我们可以 动态调整请求队列数来提高效率,默认的请求队列数存放在/sys/block/xvda/queue/nr_requests 文 ...

  7. R语言学习——矩阵

    > #矩阵是一个二维数组,每个元素都拥有相同的模式(数值型.字符型或者逻辑型).通过matrix()创建,一般使用格式为:mymatrix<-matrix(vector,nrow=numb ...

  8. mysql 提高一 动态sql 传变量

    1.需求 DELIMITER $$ SECOND STARTS '2018-09-07 08:00:00' ON COMPLETION PRESERVE ENABLE DO BEGIN ) DEFAU ...

  9. 20175229许钰玮 2018-2019-2《Java程序设计》结对编程项目-四则运算 第一周 阶段性总结

    20175229许钰玮 2018-2019-2<Java程序设计>结对编程项目-四则运算 第一周 阶段性总结 需求分析 自动生成四则运算题目(加.减.乘.除). 既可以用前缀算法(波兰算法 ...

  10. git 命令积累

    git status # 查看仓库的状态 git add . # 监控工作区的状态树,使用它会把工作时的所有变化提交到暂存区,包括文件内容修改(modified)以及新文件(new),但不包括被删除的 ...