洛谷P5289 皮配
解:观察一波部分分。
首先小数据直接暴力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 皮配的更多相关文章
- 洛谷P5289 [十二省联考2019]皮配(01背包)
啊啊啊边界判错了搞死我了QAQ 这题是一个想起来很休闲写起来很恶心的背包 对于\(k=0\)的情况,可以发现选阵营和选派系是独立的,对选城市选阵营和学校选派系分别跑一遍01背包就行了 对于\(k> ...
- 洛谷P4382 劈配
不知道这个Zayid是谁... 题意: 有n个人,m个导师.每个导师能接纳bi个人,每个人对于这m个导师都有一个志愿档次. 优先满足靠前的人,问到最后每个人匹配的导师是他的第几志愿. 每个人又有一个限 ...
- 洛谷P4382 [八省联考2018]劈配(网络流,二分答案)
洛谷题目传送门 说不定比官方sol里的某理论最优算法还优秀一点? 所以\(n,m\)说不定可以出到\(1000\)? 无所谓啦,反正是个得分题.Orz良心出题人,暴力有70分2333 思路分析 正解的 ...
- luogu P5289 [十二省联考2019]皮配 背包
LINK:皮配 我承认是一道很难的题目. 不过对于这道题 部分分的提示显得尤为重要. 首先是 40分的暴力dp 很容易想 但是不容易写. 从40分可以发现我们只需要把蓝阵营和鸭派系的人数给存在起来就行 ...
- 【BZOJ5498】[十二省联考2019]皮配(动态规划)
[BZOJ5498][十二省联考2019]皮配(动态规划) 题面 BZOJ 洛谷 题解 先考虑暴力\(dp\),设\(f[i][j][k]\)表示前\(i\)所学校,有\(j\)人在某个阵营,有\(k ...
- 【洛谷P1352】没有上司的舞会
[洛谷P1352]没有上司的舞会 x舷售 锚」翅θ 但是 拙臃 蓄ⅶ榔 暄条熨卫 翘ヴ馇 表现无愧于雪月工作室的核心管理 爸惚扎掬 颇瓶 芟缆肝 貌痉了 洵┭笫装 嗝◇裴腋 褓劂埭 ...
- 洛谷P1991 无线通讯网
P1991 无线通讯网 170通过 539提交 题目提供者洛谷OnlineJudge 标签图论 难度普及+/提高 提交该题 讨论 题解 记录 最新讨论 怎么又炸了 为啥一直40!求解! UKE:inv ...
- COCI2017-2018#3 Dojave || 洛谷P4443
题目传送门............................................................................................... ...
- 【洛谷4933】大师(DP)
题目: 洛谷4933 分析: (自己瞎yy的DP方程竟然1A了,写篇博客庆祝一下) (以及特斯拉电塔是向Red Alert致敬吗233) 这里只讨论公差不小于\(0\)的情况,小于\(0\)的情况进行 ...
随机推荐
- Python第四天 流程控制 if else条件判断 for循环 while循环
Python第四天 流程控制 if else条件判断 for循环 while循环 目录 Pycharm使用技巧(转载) Python第一天 安装 shell 文件 Python第二天 ...
- AngularJS学习之旅—AngularJS 服务(八)
1.AngularJS 服务(Service) AngularJS 中你可以创建自己的服务,或使用内建服务.2.什么是服务? 在 AngularJS 中,服务是一个函数或对象,可在你的 Angular ...
- SQL语句中不同的连接JOIN
为了从两个表中获取数据,我们有时会用JOIN将两个表连接起来.通常有以下几种连接方式: JOIN or INNER JOIN(内连接) : 这两个是相同的,要求两边表同时有对应的数据,返回行,任何 ...
- Python文件操作之把臂入林
文件操作1.打开文件open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=Tru ...
- JPA之@Transient
java 的transient关键字的作用是需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中. 使用示例: ...
- linux磁盘IO读写性能优化
在LINUX系统中,如果有大量读请求,默认的请求队列或许应付不过来,我们可以 动态调整请求队列数来提高效率,默认的请求队列数存放在/sys/block/xvda/queue/nr_requests 文 ...
- R语言学习——矩阵
> #矩阵是一个二维数组,每个元素都拥有相同的模式(数值型.字符型或者逻辑型).通过matrix()创建,一般使用格式为:mymatrix<-matrix(vector,nrow=numb ...
- mysql 提高一 动态sql 传变量
1.需求 DELIMITER $$ SECOND STARTS '2018-09-07 08:00:00' ON COMPLETION PRESERVE ENABLE DO BEGIN ) DEFAU ...
- 20175229许钰玮 2018-2019-2《Java程序设计》结对编程项目-四则运算 第一周 阶段性总结
20175229许钰玮 2018-2019-2<Java程序设计>结对编程项目-四则运算 第一周 阶段性总结 需求分析 自动生成四则运算题目(加.减.乘.除). 既可以用前缀算法(波兰算法 ...
- git 命令积累
git status # 查看仓库的状态 git add . # 监控工作区的状态树,使用它会把工作时的所有变化提交到暂存区,包括文件内容修改(modified)以及新文件(new),但不包括被删除的 ...