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\)门,这次换/不换的答案

状态转移时,按这次申请换/不换分别考虑,若这次不换则这次的选择一定是百分百不换,那么分别算一下上一次换没换成功对答案贡献即可

若这次换那么分开考虑上次换成功与否(这决定了我们用哪一个状态转移),若上次不换则有两种情况,这次成功/不成功;若上次换则有四种情况,即这次成功与否和上次成功与否乘法原理组合一下,对答案的贡献就是它们分别要走的路程分别乘上它们时间发生的概率

方程比较窒息,建议手推一下或直接看代码

\[dp[i][j][0] = min \left\{ \begin{aligned} &dp[i - 1][j][0] + Map[c[i - 1]][c[i]] \\ &dp[i - 1][j][1] + Map[c[i - 1]][c[i]] * (1 - k[i - 1]) + Map[d[i - 1]][c[i]] * k \end{aligned} \right.
\]

\[dp[i][j][1] = \left\{ \begin{aligned} &dp[i - 1][j - 1][0] + Map[c[i - 1]][d[i ]] * k[i] + Map[c[i - 1]][c[i]] * (1 - k[i]) \\ &dp[i - 1][j - 1][1] + Map[c[i - 1]][d[i]] * (1 - k[i - 1]) * k[i] + Map[d[i - 1]][d[i]] * k[i - 1] * k[i] + Map[c[i - 1]][c[i]] * (1 - k[i]) * (1 - k[i - 1]) + Map[d[i - 1]][c[i]] * k[i - 1] * (1 - k[i]) \end{aligned} \right.
\]

因为数据范围很小,教室之间距离用\(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[0][0] = 0\\
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 解题报告的更多相关文章

  1. NOIP2016解题报告

    天天听这几道题,但其实题面都没看过.今天做一下. 每道题看懂题后基本一分钟左右就切了.D2T3想的是\(O(n\log n)\)的堆做法,至少90分吧. D1T1模拟即可. D1T2每条路径拆成到根的 ...

  2. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

  3. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...

  4. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

  5. BZOJ 1051 最受欢迎的牛 解题报告

    题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[S ...

  6. 习题:codevs 2822 爱在心中 解题报告

    这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...

  7. 习题:codevs 1035 火车停留解题报告

    本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...

  8. 习题: codevs 2492 上帝造题的七分钟2 解题报告

    这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...

  9. 习题:codevs 1519 过路费 解题报告

    今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...

随机推荐

  1. 2019-5-16-WPF-光标初始化的时候-temp-文件夹满了无法创建

    title author date CreateTime categories WPF 光标初始化的时候 temp 文件夹满了无法创建 lindexi 2019-05-16 19:16:27 +080 ...

  2. 关于print()里面的sep和end参数的使用

    print('hello', 'world') #默认用空格隔开 #hello world print('hello', 'world', sep='wuli') #sep=''可以用来设置连接的字符 ...

  3. Jenkins 搭建 .NET FrameWork 持续集成环境

    本文不赘述如何安装 Jenkins,如有需要请看之前文章,这里我们主要搭建 .Net 环境.本文是在 Windows 环境下安装的 Jenkins 进行操作     一.安装所需环境     这里我们 ...

  4. 判断有向无环图(DAG)

    1.拓扑排序 bfs 所有入度为0的先入选. 2.tarjan 1个点1个集合 3.暴力 一个点不能重新到达自己

  5. kubeadm部署一个Kubernetes集群

    kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具.这个工具能通过两条指令完成一个kubernetes集群的部署: # 创建一个 Master 节点 $ kubeadm in ...

  6. Java——单例模式初步

    1.7 单例模式初步 好书推荐:java与模式 1.7.1 什么是设计模式 设计模式是在大量的实践中总结和理论化之后优选的代码结构.编程风格.以及解决问题的思考方式.设计模式就像是经典的棋谱,不同的棋 ...

  7. Python 爬取拉钩网工作岗位

    如果拉钩网html页面做了调整,需要重新调整代码 代码如下 #/usr/bin/env python3 #coding:utf-8 import sys import json import requ ...

  8. 58 matlab 编程

    0 引言 matlab中有些东西记录一下 1 matlab coder matlab命令行窗口输入: coder 回车即可打开matlab coder 窗口.接着,matlab将引导你把matlab格 ...

  9. 判断语句 (a>b)?a:b【转载】

    文章转载自https://blog.csdn.net/hyj1996818/article/details/81783513 今天刷题有看到一种我没学过的判断语句 感觉很高级的样子 我跟大家分享下我的 ...

  10. macOS cataline 10.15 升级后问题一览

    1. git无法使用.报错如下 xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), m ...