洛谷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\)的情况进行 ...
随机推荐
- php+ajax实现登录按钮加载loading效果
php+ajax实现登录按钮加载loading效果,一个提高用户体验,二个避免重复提交表单,ajax判断加载是否完成. 登录表单 <form onsubmit="return chec ...
- 转摘Linux命令工作中常用总结
Linux命令工作中常用总结 1. 搜索 在vi和vim中如果打开一个很大的文件,不容易找到对应的内容,可以使用自带的搜索关键字进行搜索定位: 在vi和vim界面中输入:"/" ...
- .net向文件写入字符串流内存溢出的问题
字符串过大导致抛出异常: exceptopm of type 'system.outOfmemoryexception' was thrown 解决方法:逐块写入可以避免这个问题
- 初学Django项目可能会遇到的问题
1. 出现莫名其妙的 app01 我项目中的app名字并不是app01,可是运行python manage.py makemigrations的时候总是提示app01不是已安装的app Applyin ...
- Loj #3056. 「HNOI2019」多边形
Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...
- 移动端1px的解决办法之styled
做项目的时候总结了一个styled中解决移动端项目1px像素线的问题,封装了一个函数,大家可以直接使用,很方便. 1 import styled from 'styled-components' co ...
- 手把手教你实现Android RecyclerView上拉加载功能
摘要 一直在用到RecyclerView时都会微微一颤,因为一直都没去了解怎么实现上拉加载,受够了每次去Github找开源引入,因为感觉就为了一个上拉加载功能而去引入一大堆你不知道有多少BUG的代码, ...
- [APIO2014]序列分割
嘟嘟嘟 复习一下斜率优化,感觉已经忘得差不多了-- 这题切入点在与答案跟切的顺序无关. 证明就是假如有三段权值分别为\(x, y, z\),那么这两刀不管按什么顺序切,得到的结果都是\(xy + xz ...
- fastjson List转JSONArray以及JSONArray转List
1.fastjson List转JSONArrayList<T> list = new ArrayList<T>();JSONArray array= JSONArray.p ...
- docker 在centos6 和centos7上的区别
这些天研究了下docker,在centos6.6上装了个docker1.7.1,在centos7.6上装了个docker18.09.0 两者还是有区别的. 1.配置docker国内镜像加速 Dock ...