NOIP2016 解题报告
D1T1 玩具谜题
xjb模拟即可
#include<bits/stdc++.h>
#define N (100000+5)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c;
c = getchar();
while (!isdigit(c)) {
if (c == '-') f = -f;
c = getchar();
}
while (isdigit(c)) {
cnt = (cnt << 3) + (cnt << 1) + c - '0';
c = getchar();
}
return cnt * f;
}
int n, m;
struct node{
int opr;
char name[20];
} a[N];
int s, t, p;
int main() {
n = read(); m = read();
for (register int i = 1; i <= n; i++) {
a[i].opr = read();
scanf("%s\n", a[i].name + 1);
}
p = 1;
for (register int i = 1; i <= m; i++) {
s = read(); t = read();
if (s == a[p].opr) {
p -= t;
p += n;
p %= n;
if (p == 0) p = n;
} else {
p += t;
p %= n;
if (p == 0) p = n;
}
}
for (register int i = 1; i <= strlen(a[p].name + 1); i++) putchar(a[p].name[i]);
return 0;
}
D1T2 天天爱跑步
神仙题各色乱搞大杂烩集中营 我也不知道为什么它会出现在\(D1T2\) 单独写
D1T3 换教室
期望\(dp\),设\(dp[i][j][0/1]\)表示考虑到第\(i\)门课,已经换了\(j\)门,这次换/不换的答案
状态转移时,按这次申请换/不换分别考虑,若这次不换则这次的选择一定是百分百不换,那么分别算一下上一次换没换成功对答案贡献即可
若这次换那么分开考虑上次换成功与否(这决定了我们用哪一个状态转移),若上次不换则有两种情况,这次成功/不成功;若上次换则有四种情况,即这次成功与否和上次成功与否乘法原理组合一下,对答案的贡献就是它们分别要走的路程分别乘上它们时间发生的概率
方程比较窒息,建议手推一下或直接看代码
\]
\]
因为数据范围很小,教室之间距离用\(Floyd\)求出即可
#include<bits/stdc++.h>
#define N (2000 + 10)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
return cnt * f;
}
int n, m, v, e, a[N], b[N], Map[305][305], dis[305][305]; //2n-course, max_request = m, v = num_of_vertex, e = num_of_edge
double k[N], f[N][N][2], ans;
int x, y, w;
void get() {
n = read(), m = read(), v = read(), e = read();
for (register int i = 1; i <= v; ++i)
for (register int j = 1; j <= v; ++j)
dis[i][j] = Map[i][j] = 1e9;
for (register int i = 1; i <= n; ++i) a[i] = read();
for (register int i = 1; i <= n; ++i) b[i] = read();
for (register int i = 1; i <= n; ++i) scanf("%lf", &k[i]);
for (register int i = 1; i <= e; ++i) {
x = read(), y = read(), w = read();
dis[x][y] = dis[y][x] = Map[x][y] = Map[y][x] = min(Map[x][y], w);
}
}
int main() {
get();
for (register int i = 1; i <= v; ++i) dis[i][i] = Map[i][i] = 0;
for (register int p = 1; p <= v; ++p)
for (register int i = 1; i <= v; ++i)
for (register int j = 1; j <= v; ++j) dis[i][j] = min(dis[i][j], dis[i][p] + dis[p][j]);
for (register int i = 0; i <= n; ++i)
for (register int j = 0; j <= m; ++j) f[i][j][0] = f[i][j][1] = 1e9;
f[1][0][0] = f[1][1][1] = 0;
for (register int i = 2; i <= n; ++i) f[i][0][0] = f[i - 1][0][0] + dis[a[i - 1]][a[i]];
for (register int i = 2; i <= n; ++i)
for (register int j = 1; j <= m; ++j) {
f[i][j][0] = min(f[i][j][0], min(f[i - 1][j][0] + dis[a[i - 1]][a[i]], f[i - 1][j][1] + (1 - k[i - 1]) * dis[a[i - 1]][a[i]] + k[i - 1] * dis[b[i - 1]][a[i]]));
f[i][j][1] = min(f[i][j][1],
min(f[i - 1][j - 1][0] + dis[a[i - 1]][a[i]] * (1 - k[i]) + dis[a[i - 1]][b[i]] * k[i],
f[i - 1][j - 1][1] + dis[b[i - 1]][a[i]] * (1 - k[i]) * k[i - 1] + dis[b[i - 1]][b[i]] * k[i] * k[i - 1] + dis[a[i - 1]][a[i]] * (1 - k[i - 1]) * (1 - k[i]) + dis[a[i - 1]][b[i]] * (1 - k[i - 1]) * k[i])) ;
}
ans = 1e9;
for (register int i = 0; i <= m; ++i) {
if (f[n][i][0] < ans) ans = f[n][i][0];
if (f[n][i][1] < ans) ans = f[n][i][1];
}
printf("%.2lf", ans);
return 0;
}
D2T1 组合数问题
组合恒等式求组合数+二维前缀和统计答案
#include<bits/stdc++.h>
#define int long long
#define N (2000 + 10)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
return cnt * f;
}
int n, m, k, T;
int c[N][N], ans[N][N];
void pre_work() {
c[1][1] = c[0][0] = c[1][0] = 1;
for (register int i = 2; i <= 2000; ++i) {
c[i][0] = 1;
for (register int j = 1; j <= i; ++j) {
c[i][j] = (c[i - 1][j] % k + c[i - 1][j - 1] % k) % k;
ans[i][j] = ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1] + (!c[i][j]) ;
}
ans[i][i + 1] = ans[i][i];
}
}
signed main() {
T = read(), k = read();
pre_work();
while (T--){
n = read(), m = read();
printf("%lld\n", m < n ? ans[n][m] : ans[n][n]);
}
return 0;
}
D2T2 蚯蚓
乍一看以为是个优先队列的\(sb\)题,冷静看了看数据范围发现优先队列过不去
发现题目隐含着很妙的单调性,因为每次取的是最长的蚯蚓切开,切的方式(切割的系数)是一样的,那么一定有先切开的蚯蚓比后切开的蚯蚓长这个结论
所以开三个单调队列,一个维护原来的蚯蚓,一个维护\(\lfloor px \rfloor\),一个维护\(x - \lfloor px \rfloor\),每次取出三个队列的队头比较一下再切割即可
再维护一下时间增长,查询时直接把增量加上去,每次切割出的两条新蚯蚓不会得到这一秒的增长量,那么在它们的值上减一下即可
#include<bits/stdc++.h>
#define int long long
#define N (8000000 + 50)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
return cnt * f;
}
int n, m, q, u, v, t, ans, x, siz, a[N];
double p;
int q1[N], q2[N], q3[N];
int h1 = 1, h2 = 1, h3 = 1, t1, t2, t3;
priority_queue<int> Q;
inline int Max(int a, int b, int c) {return max(max(a, b), c);}
inline int get_Max() {
if (Max(q1[h1], q2[h2], q3[h3]) == q1[h1]) {
if (h1 <= t1) return q1[h1];
else if (q2[h2] > q3[h3] && h2 <= t2) return q2[h2];
else return q3[h3];
}
if (Max(q1[h1], q2[h2], q3[h3]) == q2[h2]) {
if (h2 <= t2) return q2[h2];
else if (q1[h1] > q3[h3] && h1 <= t1) return q1[h1];
else return q3[h3];
}
if (Max(q1[h1], q2[h2], q3[h3]) == q3[h3]) {
if (h3 <= t3) return q3[h3];
else if (q2[h2] > q1[h1] && h2 <= t2) return q2[h2];
else return q1[h1];
}
}
inline void POP() {
if (get_Max() == q1[h1]) {
if (h1 <= t1) ++h1;
else if (q2[h2] > q3[h3] && h2 <= t2) ++h2;
else ++h3;
return;
}
if (get_Max() == q2[h2]) {
if (h2 <= t2) ++h2;
else if (q1[h1] > q3[h3] && h1 <= t1) ++h1;
else ++h3;
return;
}
if (get_Max() == q3[h3]) {
if (h3 <= t3) ++h3;
else if (q2[h2] > q1[h1] && h2 <= t2) ++h2;
else ++h1;
return;
}
}
signed main() {
// freopen("1.in", "r", stdin);
n = read(), m = read();
if (m > 1000000) {
q = read(), u = read(), v = read(), t = read();
p = (double)u / v;
for (register int i = 1; i <= n; ++i) a[i] = read();
sort (a + 1, a + n + 1);
for (register int i = n; i >= 1; --i) q1[++t1] = a[i];
for (register int i = 1; i <= m; ++i) {
if (i % t == 0) {
ans = get_Max();
printf("%lld ", ans + (i - 1) * q);
}
x = get_Max() + (i - 1) * q;
POP();
q2[++t2] = x - (int)(p * (double) x) - i * q, q3[++t3] = (int)(p * (double) x) - i * q;
}
printf("\n");
for (register int i = 1; i <= n + m; ++i) {
if (i % t == 0) printf("%lld ", get_Max() + m * q);
POP();
}
} else {
q = read(), u = read(), v = read(), t = read();
p = (double)u / (double) v;
for (register int i = 1; i <= n; ++i) x = read(), Q.push(x);
for (register int i = 1; i <= m; ++i) {
if (i % t == 0) {
ans = Q.top();
printf("%d ", ans + (i - 1) * q);
}
x = Q.top() + (i - 1) * q; Q.pop();
Q.push((int)(p * (double)x) - i * q), Q.push(x - (int)(p * (double)x) - i * q);
}
printf("\n");
siz = Q.size();
for (register int i = 1; i <= siz; ++i) {
if (i % t == 0) printf("%d ", Q.top() + m * q);
Q.pop();
}
}
return 0;
}
D2T3 愤怒的小鸟
神仙状压,很多题解的做法复杂度是假的
记录一下分析过程
因为这一年别的题没有dp所以我们考虑dp
看看数据范围,\(n \leq 18\),考虑怎么状压一下
参照洛谷第一篇题解的思路,想想怎么样设计状态
设\(dp[S]\)表示已经死了的猪的集合为\(S\),要达到这个状态\(S\)所需要发射的鸟有多少只
首先两两预处理一下经过这两点的抛物线解析式(反正只可能是形如\(ax^2 + bx\)的形式,怎么瞎搞都可),注意跳过\(x\)相等的情况,然后把这两点塞进\(line[a][b]\)这个集合内,\(line[a][b]\)表示抛物线\(ax^2 + bx\)经过的所有点的集合状态
考虑一次打一条抛物线的情况,一次打一只猪的情况以及初始状态,得到最朴素的状态转移方程
dp[S|line[i][j]] = min(dp[S] + 1)\\
dp[S|(1<<(i - 1))] = min(dp[S] + 1)\\
\]
冷静分析一波发现这个的复杂度是\(O(Tn^2 2^n)\)的,然鹅并跑不过
考虑优化
对于每个集合\(S\),我们发现第一只打不着的猪(即满足\(S\&(1<<(x - 1))\)的最小正整数\(x\)),即使我们现在转移的时候不打它,之后也得回过头来把它打掉,于是预处理一下就能做到\(O(Tn2^n)\),不过这里没有预处理,但也差不多
#include<bits/stdc++.h>
#define INF (1000000000 + 7)
#define N (20 + 5)
#define eps 1e-10
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
return cnt * f;
}
int t, n, m, f[2000000], num[N][N];
double x[N], y[N];
bool same(double x, double y) {return fabs(x - y) < eps;}
int main() {
t = read();
while (t--) {
n = read(), m = read();
for (register int i = 1; i <= n; ++i) scanf("%lf%lf", &x[i], &y[i]);
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= n; ++j) num[i][j] = 0;
for (register int i = 1; i < n; ++i)
for (register int j = i + 1; j <= n; ++j) {
if (same(x[i], x[j])) continue;
double a = (y[j] / x[j] - y[i] / x[i]) / (x[j] - x[i]);
if (a >= 0) continue;
double b = y[i] / x[i] - a * x[i];
for (register int k = 1; k <= n; ++k)
if (same(a * x[k] + b, y[k] / x[k])) num[i][j] |= (1 << (k - 1));
}
for (register int i = 0; i <= (1 << n) - 1; ++i) f[i] = INF; f[0] = 0;
for (register int i = 0; i <= (1 << n) - 1; ++i)
for (register int j = 1; j <= n; ++j) if (!(i & (1 << j - 1))) {
for (register int k = j; k <= n; ++k) {
if (j == k) f[i|(1 << (j - 1))] = min(f[i | (1 << (j - 1))], f[i] + 1);
if (same(x[j], x[k])) continue;
f[i | num[j][k]] = min(f[i | num[j][k]], f[i] + 1);
}
break;
}
printf("%d\n", f[(1 << n) - 1]);
}
return 0;
}
NOIP2016 解题报告的更多相关文章
- NOIP2016解题报告
天天听这几道题,但其实题面都没看过.今天做一下. 每道题看懂题后基本一分钟左右就切了.D2T3想的是\(O(n\log n)\)的堆做法,至少90分吧. D1T1模拟即可. D1T2每条路径拆成到根的 ...
- NOIP2016提高组解题报告
NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合
- 二模13day1解题报告
二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...
- CH Round #56 - 国庆节欢乐赛解题报告
最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...
- BZOJ 1051 最受欢迎的牛 解题报告
题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4438 Solved: 2353[S ...
- 习题:codevs 2822 爱在心中 解题报告
这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...
- 习题:codevs 1035 火车停留解题报告
本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...
- 习题: codevs 2492 上帝造题的七分钟2 解题报告
这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...
- 习题:codevs 1519 过路费 解题报告
今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...
随机推荐
- ORACLE check view
select owner as schema_name, view_name from sys.all_views where VIEW_NAME like 'P%' order by ...
- 41. wait notify 方法
wait() 等待,如果一个线程执行了wait方法,那么该线程就会进去一个以锁对象为标识符的线程池中等待 notity() 唤醒,如果一个线程执行了notity方法,那么就会唤醒以锁对象为标识符的线 ...
- Dart编程运算符
表达式是一种特殊类型的语句,它计算为一个值.每个表达都由 操作数 - 表示数据 运算符 - 定义如何处理操作数以生成值. 考虑以下表达式 2 + 3.在该表达式中,2和3是操作数,符号+(加号)是 运 ...
- Spring-Security (学习记录三)--读取数据库中的用户和角色
目录 1.先将hibernate的环境整合进来 2.创建一个数据库security,执行security.sql 3.修改spring-security.xml,采用数据库的方式读取用户跟角色 4.u ...
- C++之运算符重载(一元)
一.-符号重载 1.成员函数重载负号 2.友元函数重载负号 3.补充说明 <1> <一元运算符编码实现(一)>课程笔记: A:事实上,我们的重载运算符返回void.返回对象本身 ...
- 基于Netty的RPC架构学习笔记(八):protocol buff学习使用
文章目录 简介 准备 protobuf配置文件 生成java代码 举个
- IntelliJ IDEA创建springboot项目
1.创建新项目. 2. 3.Group 是包名,Artifact是项目名. 4.springboot版本尽量选择最高版本,且不要选择SNAPSHOP版本. 5.路径可自定义,默认为D://IDEA/M ...
- 3-MySQL高级-事务-命令(2)
事务命令 表的引擎类型必须是innodb类型才可以使用事务,这是mysql表的默认引擎 查看表的创建语句,可以看到engine=innodb -- 选择数据库 use jing_dong; -- 查看 ...
- java 与日期转换相关的方法(java.util.date类型和java.sql.date类型互相转换)、随机字符串生成方法、UUID生产随机字符串
package com.oop.util; import java.text.*; import java.util.UUID; import org.junit.Test; /* * 与日期相关的工 ...
- shell 命令 查找命令find,grep
1.find 查找文件 [ find -name 文件名 ] 在当前目录及子目录中找这个文件 [ find -iname 文件名 ] 在当前目录及子目录中找这个文件,不区分大小写 [ find -na ...