NOIP2018提高组省一冲奖班模测训练(一)
比赛链接
https://www.51nod.com/contest/problemList.html#!contestId=72&randomCode=147206
这次考试的题非常有质量
这次考试暴露了非常多的问题,心理给自己设限,知识点不熟练等等问题。
只拿了暴力的分。
奈芙莲的护符
Nephren 有n个护符,每个护符的魔力容量都是无限的,并且每个护符在初始时已经被倾注了一些魔力。Nephren想要获得里面所有的魔力,但是她最后只能选择k个护符吸收。
所以,她需要将一些护符的魔力融合到一起。但是把i号护符的魔力移动到j号杯子需要花费c[i][j]的体力。
所以请您求出最小花费的体力。
【数据范围】
40%的数据保证n<=10。
100%的数据保证1≤ k≤ n ≤ 20
所有的c[i][j]<=100000,c[i][i]=0.
第一行,输入n,k 下面N行,每行N个整数,描述c[i][j].
输入一个整数,即所需的最小体力值。
3 3 0 1 1 1 0 1 1 1 0
0
这道题复习状压dp,状压dp一般可以处理一个集合内的问题
这道题是第三题,但我放在这一题
因为这一题反而最水……
我当时一看到数据范围20,马上想到状压dp
但是一方面因为觉得这是第三题,应该会比较难,而且前面两道题都做不出来,所以心理障碍
很大,就没有怎么很深入的去想(不过说到底还是状压dp不熟练)。
考完后,5分钟秒了,发现这是我做过最水的状压dp题。
这道题和TSP问题很类似(不懂的可以百度一下,或者我博客里面有)
用1表示魔力还在,0表示没有了。
那么dp[S] = min(dp[S], dp[S^(1<<j)] + c[j][i])
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std;const int MAXM = (1 << 20) + 10; const int MAXN = 30; int c[MAXN][MAXN], dp[MAXM];int num(int x) { return !x ? 0 : 1 + num(x & (x - 1)); } //二进制中1的个数 int main() {int n, k;scanf("%d%d", &n, &k);REP(i, 0, n)REP(j, 0, n)scanf("%d", &c[i][j]);int ans = 1e9;memset(dp, 0x3f, sizeof(dp)); //这里初始化要注意 dp[(1<<n)-1] = 0;for(register int S = (1 << n) - 1; S >= 0; S--){if(num(S) < k) continue;REP(i, 0, n) if(S & (1 << i))REP(j, 0, n) if(!(S & (1 << j)))dp[S] = min(dp[S], dp[S^(1<<j)] + c[j][i]);if(num(S) == k) ans = min(ans, dp[S]); }printf("%d\n", ans);return 0; }
珂朵莉的旅行
浮游大陆由n个浮游岛构成,其中不同的浮游岛之间存在着飞艇航线。
由于浮游大陆的经济比较紧张,花在交通的费用不能太多,因此在保证每一个浮游岛都是联通的基础上,大贤者只修筑了n-1条航线。
在击败第五号岛的兽之后,威廉决定带辛苦战斗的珂朵莉,奈芙莲,艾瑟雅去浮游大陆旅行。
“Are you going toScarborough Fair?”
每个浮游岛本质属于不同的政体,所以它们的政策等会存在诸多差异。在旅行之前,威廉将浮游岛划分成两种,可以认为权值为1和0.由于要保证这是一次开心的旅行,所以他希望旅行之后经过的路径浮游岛的权值异或和为0.
他们的旅行方案是这样的:从某一个节点开始,不经过重复的节点,随机的选择一个与当前节点相连的节点,直到走到无路可走,这算完成一次旅行。
由于威廉也才来到浮游大陆不久,所以他也不知道每一个节点的权值到底是1还是0.
他想问问你,总共有多少种可能的钦点某些浮游岛的权值为1(其他浮游岛权值为0)的方式,符合上文提出的条件?
由于他们没有学过数学,因此请你将答案对 109+7 (一个质数)取模。
对于20%的数据,满足1<=n<=10
对于40%的数据,满足1<=n<=1000
对于额外20%的数据,浮游岛的连接将会成为一条链。
对于100%的数据,1<=n<=10^6.
【样例解释】
第一行一个整数n,表示浮游岛的数量 第二行开始每行两个整数u,v,表示浮游岛u和浮游岛v有无向的飞艇路线相连。
一行一个整数,表示答案。
3 1 2 2 3
10
关于异或和,如果1的个数是偶数,那就是0,如果是奇数,就是1,和0取异或就不变,和1取异或就取反n方的数据可以用树形dp过我考试的时候dp方程都推出来了,可是竟然没有写,觉得是错的(我到底在干嘛???)(树形dp不熟练)dp[i][0]表示从子树到子树根的路径异或和0的方案数,dp[i][1]是异或和为1如果是dp[i][0]如果根为1,那么除根以外的路径就要异或和为1,同时根据乘法原理,方案数是dp[v1][1] * dp[v2][1]……如果根为0,那么除根以外的路径就要异或和为0,同时根据乘法原理,方案数是dp[v1][1] * dp[v2][1]……然后根据加法原理,把这两个加起来所以dp[i][0] = (dp[v1][1] * dp[v2][1]……)+(dp[v1][1] * dp[v2][1]……) dp[i][1]也类似,然后可以发现是一样的dp[i][1] = (dp[v1][1] * dp[v2][1]……)+(dp[v1][1] * dp[v2][1]……) 但是不知道为什么只过了2个点,会RE2个点
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std;typedef long long ll; const int MAXN = 1e6 + 10; const int mod = 1e9 + 7;struct Edge{ int to, next; }; Edge e[MAXN << 1]; int head[MAXN], tot;int d[MAXN], n; ll dp[MAXN][2];void read(int& x) {int f = 1; x = 0; char ch = getchar();while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }x *= f; }void AddEdge(int from, int to) {e[tot] = Edge{to, head[from]};head[from] = tot++; }void dfs(int u, int fa) {if(d[u] == 1 && fa != -1){dp[u][1] = dp[u][0] = 1;return;}ll a = 1, b = 1;for(int i = head[u]; ~i; i = e[i].next){int v = e[i].to;if(v == fa) continue;dfs(v, u);a = a * dp[v][1] % mod;b = b * dp[v][0] % mod;}dp[u][1] = (dp[u][1] + a + b) % mod;dp[u][0] = (dp[u][0] + a + b) % mod; }int main() {read(n);if(n == 1) { puts("1"); return 0; }memset(head, -1, sizeof(head)); tot = 0;REP(i, 1, n) {int u, v;read(u); read(v);AddEdge(u, v); AddEdge(v, u); d[u]++; d[v]++;}ll ans = 0;_for(i, 1, n){memset(dp, 0, sizeof(dp));dfs(i, -1);ans = (ans + dp[i][0]) % mod;}printf("%lld\n", ans);return 0; }
然后我考虑20分一条链的做法其实这个部分分的做法再拓展一下就是满分做法了0-0-0-0-0-……0-0-0-0-0这是一条链我们分两部分来考虑,端点和除了端点以外的点 如果是除了端点以外的点0-0-0-0-0-……0-0-0-0-0设总的点数为n,那么这样的点有n-2个考虑其中一个点中间n-2个点的方案有2^(n-2)个对于其中一种方案其中一个点到端点之前的一个点的异或和是确定的那么端点的权值就是被动确定的比如从当前点到端点之前的一个点的异或和为0那么端点只能赋值为0,因为总的异或和为0所以端点的值是被动确定的。因此对于每一种方案,都可以通过调整端点的值来使得异或和为0,且端点只有一种取值也就是说,2^(n-2)全部成立那么这只是对于其中一个点,那么对于n-2个点就有(n-2)*2^(n-2)种方案 如果是端点分析方法类似对于其中一个端点,其他的点有2^(n-1)种方案端点本身被动确定那么有2个端点,也就是2*2^(n-1)方案 最后加起来就好了2*2^(n-1) + (n-2)*2^(n-2)= 2^(n-2) * (n+2) 这就是20分的做法。其实我没想出来主要思维能力没有达到,还需要多做题 100分的做法只是把端点的数变成叶子的数罢了推理的方式很像,把2(2个端点)换成k(k个叶子)就好了所以答案为2^(n-k) * (n+k)读者可以自己推一遍(我懒) 最后注意要特判一下n=1的情况,有点坑
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std;typedef long long ll; const int MAXN = 1e6 + 10; const int mod = 1e9 + 7; int d[MAXN], n;void read(int& x) {int f = 1; x = 0; char ch = getchar();while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }x *= f; }ll pow(ll a, int b) {ll res = 1 % mod; a %= mod;for(; b; b >>= 1){if(b & 1) res = res * a % mod;a = a * a % mod;}return res; }int main() {read(n);if(n == 1) { puts("1"); return 0; }REP(i, 1, n) {int u, v;read(u); read(v);d[u]++; d[v]++;}int k = 0;_for(i, 1, n)if(d[i] == 1) k++;ll ans = 2;ans = pow(ans, n - k);ans = ans * (n + k) % mod;printf("%lld\n", ans);return 0; }
奈芙莲的序列
有一天可爱的Nephren得到了一个序列,一开始,她取出序列的第一个数,形成一个新的序列B,然后取出序列A的第二个数,放在序列B的最左边或最右边,序列B此时有两个数,下一步,再取出序列A的第三个数放在序列B的最左边或最右边,……
现在的问题是,通过上面的步骤,可以得到B的最长上升子序列的长度是多少
【数据规模及约定】
30%的数据保证N<=20
50%的数据保证N<=1000
100%的数据保证1 ≤ N ≤ 2×105
保证a序列所有数不会超过 109
第一行,一个整数N. 第二行,N个整数,表示序列A。
一行一个整数,表示最长上升子序列的长度
4 2 1 3 4
4
这道题的关键就是要推出一个性质,同时会用树状数组求LIS(二分的方法不行)什么性质呢?我们自己多造几组数据来算可以发现(然而我考试的时候并没有发现)新生成的b序列的最长上升子序列的长度>=a序列的最长上升子序列长度(我们要考虑原序列和新序列的关系)比如样例 2 1 3 4如果一直放最右边,那么生成的序列和原来一样那么我们可以通过某种策略来使新序列答案更大对于这道题,可以21 21 2 31 2 3 4那么这种策略是什么呢,这是这道题的关键拿样例来看,为什么新生成序列答案更大答案中比原来序列的最长上升子序列多了一个1这个1怎么来的。原序列中在2的右边,然后放到2的左边来的。1满足在2的右边,又比2小最长下降子序列???那么我们是不是可以猜一下新序列的最长上升子序列等于以a[i]为起始的最长上升子序列+a[i]为起始的最长下降子序列-1(多算了一次a[i])我们这么想先说最长上升子序列,我们可以把这一部分的数从左到右一直放到最右边,那么这一部分的数就是有效的然后是最长下降子序列,我们可以把这一部分的数从左到右一直放到最左边,那么这一部分的数也是是有效的 比如 6 5 4 3 7 8 9那么就是65 64 5 63 4 5 63 4 5 6 73 4 5 6 7 83 4 5 6 7 8 9这个数据可能有点水,但是就算最长上升子序列和最长下降子序列有交叉,也是成立的。遇到了最长上升子序列中的数,就放最右边,下降就最左,其他的数无所谓那么上面那个结论就成立了,也就是新序列的最长上升子序列等于以a[i]为起始的最长上升子序列+a[i]为起始的最长下降子序列-1(多算了一次a[i])那么枚举a[i]求max就好了。 那么这两个数组怎么求呢可以n方暴力,拿50分100分要用树状数组不知道怎么用树状数组优化到nlogn的同学可以看我写的这篇博客https://www.cnblogs.com/sugewud/p/9823222.html 然后这里有个细节,就是求最长上升的时候把a[i]变成-a[i]但是下标要>=1所以改成m - a[i] + 1(m为离散化后最大的数值)但是因为是严格上升,加入的时候要写成m - a[i] + 0这个时候下标就会为0了所以改成m - a[i] + 2, 加入的时候写m - a[i] + 1
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std;const int MAXN = 2e5 + 10; int a[MAXN], b[MAXN], n, m; int dp[MAXN][2], f[MAXN];inline lowbit(int x) { return x & (-x); }void motify(int x, int p) {for(; x <= m + 1; x += lowbit(x))f[x] = max(f[x], p); }int get_max(int x) {int res = 0;for(; x; x -= lowbit(x))res = max(res, f[x]);return res; }int main() {scanf("%d", &n);_for(i, 1, n) scanf("%d", &a[i]), b[i] = a[i];sort(b + 1, b + n + 1);m = unique(b + 1, b + n + 1) - b - 1;_for(i, 1, n) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;for(register int i = n; i >= 1; i--){dp[i][0] = get_max(m - a[i] + 1) + 1;motify(m - a[i] + 2, dp[i][0]);}memset(f, 0, sizeof(f));for(register int i = n; i >= 1; i--){dp[i][1] = get_max(a[i] - 1) + 1;motify(a[i], dp[i][1]);}int ans = 0;_for(i, 1, n)ans = max(ans, dp[i][0] + dp[i][1] - 1);printf("%d\n", ans);return 0; }
总结:收货还是蛮大的T1 思维方式+复习树形dpT2 手算样例找规律猜结论+学会树状数组求LIST3 复习状压dp
转载于:https://www.cnblogs.com/sugewud/p/9822933.html
NOIP2018提高组省一冲奖班模测训练(一)相关推荐
- 51Nod NOIP2018提高组省一冲奖班模测训练
51Nod NOIP2018提高组省一冲奖班模测训练 NOIP2018提高组省一冲奖班模测训练1 T1 珂朵莉的旅行 T2 奈芙莲的序列 T3 奈芙莲的护符 NOIP2018提高组省一冲奖班模测训练2 ...
- NOIP2018提高组省一冲奖班模测训练(三)
NOIP2018提高组省一冲奖班模测训练(三) 自己按照noip的方式考,只在最后一两分钟交了一次 第一题过了,对拍拍到尾. 第二题不会.考试时往组合计数的方向想,推公式,推了一个多小时,大脑爆炸,还 ...
- NOIP2018提高组省一冲奖班模测训练2 T3 XYK的音游
10月22日NOIP2018提高组省一冲奖班模测训练2 T3 XYK的音游 题目描述 XYK最近入坑了一个新音游. 游戏界面上有Ñ个并排的按键,当前这首歌有米个鼓点.游戏的玩法是在鼓点的时刻移动鼠标到 ...
- NOIP2018提高组省一冲奖班模测训练(二)
比赛链接 NOIP2018提高组省一冲奖班模测训练(二) 今天发挥正常,昨天不在状态-- 花了很久A了第一题 第二题打了30分暴力 第三题投机取巧输出test1答案(连暴力都不知道怎么打,太弱了) 2 ...
- NOIP2018提高组省一冲奖班模测训练3 T2 XYG的蛋糕
10月27日NOIP2018提高组省一冲奖班模测训练3 T2 XYG的蛋糕 题目描述 XYG要过生日了,他准备了一个n×m的矩形蛋糕请大家吃. 切蛋糕的方法如下:每次选出已经分出的某一个矩形蛋糕,一刀 ...
- [51Nod]NOIP2018提高组省一冲奖班模测训练(一)题解
http://www.51nod.com/contest/problemList.html#!contestId=72&randomCode=147206 原题水题大赛.. A.珂朵莉的旅行 ...
- NOIP2018提高组比赛总结
NOIP2018提高组比赛总结 前言 新赛季,依旧有很多失误. 在些许的遗憾和无奈中,NOIP2018,撒花结束 纵观今年的整一场NOIP,有许多值得总结的地方 正文 NOIP2018初赛 第二次参加 ...
- NOIP2018 提高组游记
NOIP2018 提高组游记的重点不是NOIP而是游记!!! 本文分为 4 个部分: 1.关于2017, 以及自己的简介 2.noip2018游记 3.写给高一高二的学弟学妹 4.写给高三的同学和自己 ...
- P5049 [NOIP2018 提高组] 旅行
P5049 [NOIP2018 提高组] 旅行 题意: 一棵树(可能是基环树),从1出发,每到达一个新的点就记录下编号.求一种走法使得记录下来的编号字典序最小. 1≤n≤500000 m=n−1 或 ...
最新文章
- java 读取流的字符编码格式_如何使用Java代码获取文件、文件流或字符串的编码方式...
- 关于ASP.NET MVC P5中CheckBox的HtmlHelper方法的bug。
- 【杂谈】从学生到讲师,我如何20天里在有三AI赚3万
- Win10_MySQL环境搭建以及Navicat的使用全解
- DRAM, SRAM, SDRAM 的关系与区别
- 教程:正确的SLF4J日志记录用法以及如何检查它
- 系统新模块增加需要哪些步骤_人工智能之父的问题解决策略:模块化
- Romoting 通信DEMO(整理)
- CCF202009-5 密信与计数【AC自动机】(100分题解链接)
- laravel 效率与java_让你的Laravel 应用运行速度飞起来!(利用PHP OPcache)
- AMPL—快速了解,秒懂它。
- 第三篇:关于MVPArms与OKGO结合报错--mRootView为空
- php b框架,thinkphp_bjui
- 攻防比赛中蓝队常用的攻击方法
- android 根据基站测距,无线测距基站
- C++语法学习笔记十四:派生类-调用顺序-访问等级-函数遮蔽
- 《Excel大神上分攻略》学习笔记3——函数求和及常见函数
- python中sticky_vue-sticky组件详解
- php scws自定义词库,scws分词 自定义词库的方法
- linux双系统重启卡光标,Ubuntu 16.04安装完重启后黑屏,光标一直闪