【HNOI2013】题解 bzoj3139~bzoj3144
比赛
题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3139
题解:
3$\le$N$\le$10,比较明显是一个搜索题,一开始我是直接搜了,没有记忆化,如果先枚举每一队可以的胜负平,加上合法性判断,再进行枚举,那么是可以拿到70分的,这里有一个重要的剪枝,在枚举了每一队的情况后一定要判断胜场+负场是否相等,这里有20分。。
以下正解:
在爆搜的时候我们每一队每一队去枚举,我们尝试着记忆化。
首先我们发现,对于一组数据,得分序列(读入序列)的顺序和答案是无关的,那么我们记忆当还有x个队没有处理时,每一队的剩余得分为$a_{n - x + 1}$,$a_{n - x + 2}$,......,$a_{n}$ 这个状态对答案的贡献,通过对这个数组的排序,我们可以大量去重,甚至不需要加太多的优化都可以AC。
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drep(i, a, b) for (int i = a; i >= b; i--)
#define REP(i, a, b) for (int i = a; i < b; i++)
#define mp make_pair
#define pb push_back
#define clr(x) memset(x, 0, sizeof(x))
#define xx first
#define yy second
using namespace std;
typedef long long i64;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const i64 INF = 0x3f3f3f3f3f3f3f3fll;
template <typename T> void Max(T &a, T b) { if (a < b) a = b; }
template <typename T> void Min(T &a, T b) { if (a > b) a = b; }
//*************************************************************** int n;
struct Conditions {
int a[];
inline long long hash() {
long long ret = ;
rep(i, , n) ret = ret * + a[i];
return ret;
}
inline void Sort() { sort(a + n - a[] + , a + + n); }
} start, bound;
map <i64, i64> M;
const int mod = 1e9 + ;
long long dfs(int step, Conditions now) {
if (now.a[] == ) return M[now.hash()];
if (now.a[n - now.a[] + ] > * (n + - step)) return -;
if (step > n) {
now.a[]--;
now.Sort();
if (M[now.hash()]) return M[now.hash()];
return M[now.hash()] = dfs(n - now.a[] + , now);
}
long long res = , tmp;
int idx = n - now.a[] + ;
if (now.a[idx] >= ) {
now.a[idx] -= ;
tmp = dfs(step + , now);
if (tmp != -) (res += tmp) %= mod;
now.a[idx] += ;
}
if (now.a[idx] >= && now.a[step] >= ) {
now.a[idx] -= , now.a[step] -= ;
tmp = dfs(step + , now);
if (tmp != -) (res += tmp) %= mod;
now.a[idx] += , now.a[step] += ;
}
if (now.a[step] >= ) {
now.a[step] -= ;
tmp = dfs(step + , now);
if (tmp != -) (res += tmp) %= mod;
now.a[idx] += ;
now.a[step] += ;
}
res = res ? res : -;
return res;
}
int main() {
scanf("%d", &n);
start.a[] = n;
rep(i, , n) scanf("%d", &start.a[i]);
start.Sort();
bound.a[] = , bound.a[n] = ; M[bound.hash()] = ;
printf("%lld\n", dfs(, start));
return ;
}
消毒
题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3140
题解:
a * b * c$\le$5000,所以min(a, b, c)$\le$18。
首先如果用长度为p * q * r的立方题来框住这些点,我们可以把它变成用 1 * p * q 或者 1 * q * r 或者 p * 1 * r的立方体来覆盖,答案不会更差。
那么我们可以用来填充的无非是上面3种的立方体来覆盖,那么我们可以枚举最小的那一维,剩下的用二分图最小点覆盖。
我们把最小的那一维旋转到x轴上,我们二进制枚举这一维 复杂度为$2^{18}$,剩下的我们只能用 p * q * 1 或者 p * 1 * r 来覆盖剩下的点,那么我们把不能用x轴(1 * q * r)覆盖的点分别映射到y轴,z轴上去,之后二分图最小点覆盖,也就是最大匹配,记得matrix67大神证明过König定理 http://www.matrix67.com/blog/archives/116?replytocom=4432
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drep(i, a, b) for (int i = a; i >= b; i--)
#define REP(i, a, b) for (int i = a; i < b; i++)
#define pb push_back
#define mp make_pair
#define clr(x) memset(x, 0, sizeof(x))
#define xx first
#define yy second
using namespace std;
typedef long long i64;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const i64 INF = 0x3f3f3f3f3f3f3f3f;
template <typename T> void Max(T &a, T b) { if (a < b) a = b; }
template <typename T> void Min(T &a, T b) { if (a > b) a = b; }
//******************************************************************** const int maxn = ; struct point {
int x, y, z; point() {}
point(int _x, int _y, int _z) :
x(_x), y(_y), z(_z) {}
} one[maxn];
int cnt_one;
struct Ed {
int u, v, nx; Ed() {}
Ed(int _u, int _v, int _nx) :
u(_u), v(_v), nx(_nx) {}
} E[maxn];
int G[maxn], edtot;
void addedge(int u, int v) {
E[++edtot] = Ed(u, v, G[u]);
G[u] = edtot;
}
int A, B, C; bool t[maxn], used[maxn]; int belong[maxn];
bool dfs(int x) {
for (int i = G[x]; i; i = E[i].nx) {
if (used[E[i].v]) continue;
used[E[i].v] = ;
if (!belong[E[i].v] || dfs(belong[E[i].v])) {
belong[E[i].v] = x;
return true;
}
}
return false;
}
int ans;
void solve() {
static int hsh[maxn]; int hsh_cnt();
rep(i, , cnt_one) hsh[hsh_cnt++] = one[i].x;
sort(hsh, hsh + hsh_cnt); hsh_cnt = unique(hsh, hsh + hsh_cnt) - hsh;
REP(s, , << hsh_cnt) {
rep(i, , A) t[i] = ;
int tmp();
REP(i, , hsh_cnt) if (s >> i & ) t[hsh[i]] = , tmp++;
edtot = ; rep(i, , B) G[i] = ;
rep(i, , cnt_one) if (!t[one[i].x]) addedge(one[i].y, one[i].z);
rep(i, , C) belong[i] = ;
rep(i, , B) {
rep(j, , C) used[j] = ;
if (dfs(i)) tmp++;
if (tmp > ans) break;
}
Min(ans, tmp);
}
} int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &A, &B, &C);
cnt_one = ;
rep(i, , A) rep(j, , B) rep(k, , C) {
int id; scanf("%d", &id);
if (id == ) one[++cnt_one] = point(i, j, k);
}
if (B > A) { swap(A, B); rep(i, , cnt_one) swap(one[i].x, one[i].y); }
if (C > A) { swap(A, C); rep(i, , cnt_one) swap(one[i].x, one[i].z); }
ans = A;
solve();
printf("%d\n", ans);
}
}
旅行
题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3141
题解:
数列
题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3142
题解:
原数列$\underbrace{ a_{1},a_{2},a_{3}......a_{k} }_{k}$
构造数列$b_{i}=a_{i}-a_{i-1}$
那么我们得到$\underbrace{ b_{1},b_{2},b_{3}......a_{k-1} }_{k-1}$
我们考虑每个不同数列的不同贡献。
贡献为:$n-b_{1}-b_{2}-b_{3}-b_{4}-b_{k-1}$
因为$m*(k-1)>n$所以每一个数列都有如上贡献,共有$m^{k-1}$种情况
那么求和
n的贡献是$n*m^{k-1}$
对于剩下的每一项,任取一项,在这一项取1时,共有$m^{k-2}$个数列,取2时,共有$m^{k-2}$个数列......
那么对于$b_{k}$对答案的贡献是$(\sum_{i=1}^{m})*m^{k-2}$
所以总答案为$n*m^{k-1}+(k-1)*(\sum_{i=1}^{m})*m^{k-2}$
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drep(i, a, b) for (int i = a; i >= b; i++)
#define REP(i, a, b) for (int i = a; i < b; i++)
#define mp make_pair
#define pb push_back
#define xx first
#define yy second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const ll INT = 0x3f3f3f3f3f3f3f3fll;
template <typename T> void Max(T &a, T b) { if (a < b) a = b; }
template <typename T> void Min(T &a, T b) { if (a > b) a = b; }
//****************************************************************** ll n, m, k, p;
ll POW(ll base, ll num) {
ll ret = ;
while (num) {
if (num & ) (ret *= base) %= p;
(base *= base) %= p;
num >>= ;
}
return ret;
}
int main() {
scanf("%lld%lld%lld%lld", &n, &k, &m, &p);
ll ans1 = n % p;
(ans1 *= POW(m , k - )) %= p;
ll ans2 = (m * (m + ) / ) % p;
(ans2 *= POW(m, k - )) %= p;
(ans2 *= k - ) %= p;
printf("%lld", ((ans1 - ans2) % p + p) % p);
return ;
}
游走
题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3143
题解:
首先贪心,编号小的经过次数一定多,那么我们只需要求出每一条边的经过次数即可,因为是等可能的,那么这个不好求的经过求每一个点的经过次数$P_{i}$,那么每一条边$(x,y)$的经过次数$G_{i}=\frac{P_{x}}{degree_{x}}+\frac{P_{y}}{degree_{y}}$
那么问题转化为了如何求解$P_{i}$
我们发现$P_{i}=\sum_{\forall(x,i)}\frac{P_{x}}{degree_{x}}+\sum_{\forall(i,x)}\frac{P_{x}}{degree_{x}}$
但是有特殊情况$P_{1}=\sum_{\forall(x,i)}\frac{P_{x}}{degree_{x}}+\sum_{\forall(i,x)}\frac{P_{x}}{degree_{x}}+1$
$P_{n}=1$
那么很明显了,高斯消元,有一个地方需要注意,在消元之中,$P_{n}$应该置为0,应为只要到达n是出不来的。
$1^{-10}$才过。。。好像long double直接过的样子。。。
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drep(i, a, b) for (int i = a; i >= b; i++)
#define REP(i, a, b) for (int i = a; i < b; i++)
#define mp make_pair
#define pb push_back
#define xx first
#define yy second
#define eps 1e-10
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const ll INT = 0x3f3f3f3f3f3f3f3fll;
template <typename T> void Max(T &a, T b) { if (a < b) a = b; }
template <typename T> void Min(T &a, T b) { if (a > b) a = b; }
//****************************************************************** const int maxn = , maxm = ; double eq[maxn][maxn];
pii E[maxm]; int ed_tot;
int deg[maxn];
double A[maxm], val[maxn]; void gauss(int m, int n) {
rep(i, , n) {
if (fabs(eq[i][i]) < eps) rep(j, i, m) { if (fabs(eq[j][i]) > eps) swap(eq[i], eq[j]); break; }
rep(j, i + , n + ) eq[i][j] /= eq[i][i];
eq[i][i] = ;
rep(j, , m) if (i != j && fabs(eq[j][i]) > eps) {
double t = eq[j][i];
rep(k, i, n + ) eq[j][k] -= t * eq[i][k];
}
}
rep(i, , m) val[i] = eq[i][n + ];
} int main() {
int n, m; scanf("%d%d", &n, &m);
rep(i, , m) {
int x, y; scanf("%d%d", &x, &y);
E[++ed_tot] = mp(x, y);
deg[x]++, deg[y]++;
}
rep(i, , m) {
eq[E[i].xx][E[i].yy] = 1.0 / deg[E[i].yy];
eq[E[i].yy][E[i].xx] = 1.0 / deg[E[i].xx];
}
rep(i, , n) eq[n][i] = ;
eq[][n + ] = -;
rep(i, , n) eq[i][i] = -;
gauss(n, n);
//val[n] = 1;
rep(i, , m) {
A[i] += val[E[i].xx] / deg[E[i].xx];
A[i] += val[E[i].yy] / deg[E[i].yy];
}
sort(A + , A + + m);
double ans();
rep(i, , m) ans += (m - i + ) * A[i];
printf("%.3lf\n", ans);
return ;
}
切糕
题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3144
题解:
题意有点搞笑还以为是一个平面。
实际上是对于P*Q的每一个竖列,选一个干掉就可以了。
哈哈哈哈哈,网络流经典模型。
注意,以下$(x,y,z)$均表示第x层y行z列。
首先考虑没有D限制的情况,对于每一个点,对它的上一层建它的边权的点,即$(x, y, z)\to(x-1,y,z)\quad v(x,y,z)$
这样需要多的一层,没问题吧。如果割掉一条边相对应着选了一个点那么最小割是答案对吧。。。
考虑有D的情况。
我们必须对割进行限制,怎么做呢?
连边
$ \forall(x,y,z) \to(x-D,Y,Z) \quad (x-D \le0)&&|Y-y|+|X-x| \le1$
来举个例子,假设$D=2$,我现在选择$(4,5,6)$这个点,和选择这个点有关的边有$(7,6,6)\to(5,5,6)$和$(4, 5, 6)\to(2,6,6)$
对$(4, 5, 6)\to(2,6,6)$连边,这样连边以后从$(4,5,6)$就无法走到$(1,6,6)$这样的点了,应为这样构不成一个割。
那它的上限是怎么确定的呢?是$(7,6,6)\to(5,5,6)$来阻隔的,这样就不能让它选择大于D的点,否则构不成割,如图三。
图中左边的列表示$(x,5,6)$,右边的列表示$(x,6,6)$的列。
【HNOI2013】题解 bzoj3139~bzoj3144的更多相关文章
- Hnoi2013题解 bzoj3139~3144
话说好久没写题(解)了.. 先贴份题解:http://wjmzbmr.com/archives/hnoi-2013-%E9%A2%98%E8%A7%A3/(LJ神题解..Lazycal表示看不懂..) ...
- [HNOI2013]题解
代码在最后 [HNOI2013]比赛 记忆化搜索 把每一位还需要多少分用\(27\)进制压进\(long\) \(long\),\(map\)记忆化一下即可 [HNOI2013]消毒 先考虑在二维平面 ...
- # HNOI2012 ~ HNOI2018 题解
HNOI2012 题解 [HNOI2012]永无乡 Tag:线段树合并.启发式合并 联通块合并问题. 属于\(easy\)题,直接线段树合并 或 启发式合并即可. [HNOI2012]排队 Tag:组 ...
- BZOJ3144:[HNOI2013]切糕——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3144 看着很像网络流,但是费用流貌似无法解决这个问题,其实甚至连忽略d的情况都做不到. 最小割? ...
- 【BZOJ3139】[HNOI2013]比赛(搜索)
[BZOJ3139][HNOI2013]比赛(搜索) 题面 BZOJ 洛谷 题解 双倍经验
- 【BZOJ3144】[HNOI2013]切糕
[BZOJ3144][HNOI2013]切糕 题面 题目描述 经过千辛万苦小 A 得到了一块切糕,切糕的形状是长方体,小 A 打算拦腰将切糕切成两半分给小 B.出于美观考虑,小 A 希望切面能尽量光滑 ...
- 【BZOJ3144】[Hnoi2013]切糕 最小割
[BZOJ3144][Hnoi2013]切糕 Description Input 第一行是三个正整数P,Q,R,表示切糕的长P. 宽Q.高R.第二行有一个非负整数D,表示光滑性要求.接下来是R个P行Q ...
- bzoj3144 [HNOI2013]切糕(最小割)
bzoj3144 [HNOI2013]切糕(最小割) bzoj Luogu 题面描述见上 题解时间 一开始我真就把这玩意所说的切面当成了平面来做的 事实上只是说相邻的切点高度差都不超过 $ d $ 对 ...
- BZOJ3144 Hnoi2013 切糕 【网络流】*
BZOJ3144 Hnoi2013 切糕 Description Input 第一行是三个正整数P,Q,R,表示切糕的长P. 宽Q.高R.第二行有一个非负整数D,表示光滑性要求.接下来是R个P行Q列的 ...
随机推荐
- iOS 常用代码块
1.判断邮箱格式是否正确的代码: // 利用正则表达式验证 -( BOOL )isValidateEmail:( NSString *)email { NSString *emailRegex ...
- this的应用
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- 平移关节(Prismatic Joint)
package{ import Box2D.Common.Math.b2Vec2; import Box2D.Dynamics.b2Body; import Box2D.Dynamics.Joints ...
- 转:Selenium2.0介绍——WebDriver两种驱动浏览器的方式.
如果之前熟悉Selenium RC,理解了Selenium RC是如何工作的,那么,当第一次接触Selenium WebDriver的时候,看到WebDriver居然可以不需要指定远端服务器的IP地址 ...
- heartbeat集群安装配置
安装配置高可用集群需要注意:1.节点名称:集群每个节点的名称都得能互相解析 /etc/hosts hosts主机名的正反解析结果必须跟"uname -n"的结果保持一致2.时间必须 ...
- vue数据源转json问题
开发过程中使用到了vue框架进行前端批量数据的处理,将批量数据转换为json格式进行ajax传参时需要注意将vue数据源得到的json结果进行如下处理,webservice接收json数据时无法有效的 ...
- 通过onActivityResult()先跳转到联系人界面,然后把传回来的手机号显示到应用的EditText上
<pre name="code" class="plain"><pre name="code" class="p ...
- 手机电话号码吉凶查询原理及ASP算法源码 转
随着手机的快速普及,越来越多的人都在使用手机,而号码的挑选也是用户越来越关心的事情.虽然号码只是个代号而已,但几千年的传统积淀仍给号码赋予其各种含义,至于号码的吉凶也是见仁见智的一种个人喜好问题,或许 ...
- java调用dll-JNA
介绍 给大家介绍一个最新的访问本机代码的 Java 框架 —JNA . JNA(Java Native Access) 框架是一个开源的 Java 框架,是 SUN 公司主导开发的,建立在经典的 JN ...
- PAT (Advanced Level) 1063. Set Similarity (25)
读入之后先排序. 询问的时候可以o(m)效率得到答案. #include<cstdio> #include<cstring> #include<cmath> #in ...