解:观察一波部分分。

首先小数据直接暴力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. 会话固定攻击 - yxcms session固定漏洞

    目录 会话固定攻击 e.g. yxcms session固定攻击 分析 了解更多 会话固定攻击 Session fixation attack(会话固定攻击)是利用服务器的session不变机制,借他 ...

  2. c/c++ linux 进程间通信系列3,使用socketpair,pipe

    linux 进程间通信系列3,使用socketpair,pipe 1,使用socketpair,实现进程间通信,是双向的. 2,使用pipe,实现进程间通信 使用pipe关键点:fd[0]只能用于接收 ...

  3. 【初学必备】Win10环境下Anaconda安装

    Anaconda集合了python,Spyder,Jupyter notebook及conda-----包管理器与环境管理器(含常用的panda,numpy等),省去单独下载的繁琐步骤,方便使用. 注 ...

  4. spingboot一键部署到阿里云(Cloud Toolkit工具)

    一般做法 一键部署工具   前些天在完成一个项目时候需要将springboot项目部署到服务器上, 以下是两种做法 前面介绍的是一般做法: 后面将介绍省去这些步骤的一键部署工具Cloud Toolki ...

  5. 二叉搜索树的最近公共祖先的golang实现

    给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q 的祖先且 x ...

  6. 面向对象___str__和__repr__

    老师的博客关于此知识点 http://www.cnblogs.com/Eva-J/articles/7351812.html#_label7 __str__和__repr__ 改变对象的字符串显示__ ...

  7. PostgreSQL:Java使用CopyManager实现客户端文件COPY导入

    在MySQL中,可以使用LOAD DATA INFILE和LOAD DATA LOCAL INFILE两种方式导入文本文件中的数据到数据库表中,速度非常快.其中LOAD DATA INFILE使用的文 ...

  8. Nowcoder217D msc的背包 背包、生成函数、组合

    传送门 发现这是一个背包问题,而\(k\)又很大,考虑生成函数方式解决这个问题. 对于体积为\(1\)的物品的生成函数为\(\frac{1}{1 - x}\),体积为\(2\)的物品的生成函数为\(\ ...

  9. SpringCloud(7)服务链路追踪Spring Cloud Sleuth

    1.简介 Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可.本文主要讲述服务追踪组件zipki ...

  10. HIT创业感言:只有长寿的企业才有持续价值

    导语:本文将讨论医疗信息化行业中的创业和企业经营问题.笔者创立的南京都昌科技有限公司专做电子病历编辑器控件,已经有3年多,期间辛苦多年,但因为医疗信息化行业的整体发展良好,也能有所成就了,不过革命尚未 ...