NOIP2018提高组省一冲奖班模测训练(三)

自己按照noip的方式考,只在最后一两分钟交了一次

第一题过了,对拍拍到尾。

第二题不会。考试时往组合计数的方向想,推公式,推了一个多小时,大脑爆炸,还是一直推不出来(然而正解是dp??)

第三题打了暴力

如果是正式比赛的话,就在省一分数线徘徊。

第二题如果能拿一点部分分的话,那省一就比较稳了

正式比赛的话应该部分分会有,这套题的第二题部分分貌似拿不了

讲解讲的非常细,非常清晰。比雅礼集训听的舒服太多太多了。

Anan的派对

Anan想举办一个派对。

Anan的朋友总共有 n 人。第i个人如果参加派对会得到 ci 的快乐值,除他自己外每多一个人参加他会减少 di 的快乐值。
Anan想让这个派对的总快乐值尽可能大,在这个基础上,能来的人越多越好。
Anan想知道最佳的邀请方案的快乐值与参加人数。
对于 50% 的数据, n≤20 
对于 100% 的数据, n≤1000 
Input
第一行一个正整数n 。
第二行n个整数表示ci。
第三行n个整数表示di。
Output
第一行一个整数最大快乐值。
第二行一个整数表示最佳方案的参加人数。
Input示例
6
10 10 10 10 10 9
2 2 2 2 2 3
Output示例
18
3

1000的数据范围一般是n方,最多加两个log
这道题第一反应二分参加人数不就非常好算了吗
但是貌似参加人数不满足单调性
然后这个思路就pass掉了
然后开始绕弯子,想到搜索,想到动规……
一直绕了40多分钟……
然后突然想到,貌似直接枚举人数复杂度是n方logn的
…………
相信自己的第一感觉……
然后不要凭感觉看会不会超时,要算复杂度……
最后1个小时才做完了,排行榜上有人17分钟AC了…… 枚举出人数之后,有两个小优化(不加也能过)
(1)排序的复杂度是nlogn,而我们关心的只是前几个人的值,所以可以用nth_element
复杂度是O(n)的。
可以把n方logn 优化到n方
(2)如果有人的值是负数可以直接break,这个人不选还更优
#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 = 1e3 + ;
int c[MAXN], d[MAXN], t[MAXN], n; int main()
{
scanf("%d", &n);
_for(i, , n) scanf("%d", &c[i]);
_for(i, , n) scanf("%d", &d[i]); int ans1 = , ans2 = ;
_for(num, , n)
{
_for(i, , n) t[i] = c[i] - d[i] * (num - );
nth_element(t + , t + num, t + n + , greater<int>()); int sum = , ok = ;
_for(i, , num)
{
sum += t[i];
if(t[i] < ) { ok = ; break; }
}
if(!ok) break; if(ans1 < sum) { ans1 = sum; ans2 = num;}
else if(ans1 == sum && ans2 < num) ans2 = num;
} printf("%d\n%d\n", ans1, ans2); return ;
}

XYG的蛋糕

XYG要过生日了,他准备了一个 n×m 的矩形蛋糕请大家吃。

切蛋糕的方法如下:每次选出已经分出的某一个矩形蛋糕,一刀将其切成两个小矩形蛋糕。当然,这一刀可以选择横切或者竖切。最终XYG要把蛋糕切成 n×m 块 1×1 的小蛋糕。
XYG希望你告诉他总共有多少种不同的切法。两个切法不同当且仅当其中一个切法中存在一条刀痕,在另一个切法中不存在。
对于 40% 的数据, n,m≤15 
对于 100% 的数据, n,m≤300 
Input
一行两个正整数n,m。
Output
一行一个正整数表示方案数998244353取模。
Input示例
3 2
Output示例
4


我第一反应数学问题,应该要用到什么组合数之类的。
然后就推啊推啊推啊推啊
还是没有推出来
中间发现有子问题结构,但没有深入去想 看到300的数据范围的时候应该马上反应到正解应该是一个n三方的复杂度
正解是dp
其实我发现有子问题结构就应该深入去想dp的
我们可以发现当矩形切了之后,矩形变小了,就可以利用之前的值
可以设dp[i][j]表示i * j的矩形的方案数
显然那么我们可以枚举切在哪个位置
先考虑横着切,切在k
那么方案数是
∑dp[k][j] * dp[i-k][j] 1 <= k < i
这里用到了乘法原理,两边的矩形是分步的,所以是乘法
同理竖着切
∑dp[i][k] * dp[i][j-k] 1 <= k < k
那么根据加法原理,dp[i][j]就是把两个加起来了。 但是这么做有一个问题,会重复计算
比如第一刀切第2行,第二刀切第3行
和第一刀切第3行,第二刀切第2行
刀痕是一样的,但是算了两次
这么办呢,我推数学公式的时候也在这卡住了,想不到好的解决方案 这个时候我们想想两层for循环枚举两两之间元素的时候,我们第二层循环j通常是从i+1开始,因为这样不会重复
枚举相同的i,j。那么我们可以把这个思想迁移到这道题。
如果我们当前是横着切,那么切的这一行以前一定没有横着切过。这样可以保证不重复,竖着切同理
怎么实现呢
我们可以设dp[i][j][0~2]
dp[i][j][1]表示只是横着切的时候的方案 dp[i][j][2]表示只是竖着切的时候的方案
dp[i][j][0]表示横竖都可以切的时候的方案

那么转移方程就是
dp[i][j][1] = dp[k][j][2] * dp[i-k][j][0]
dp[i][j][2] = dp[i][k][1] * dp[i][j-k][0]
dp[i][j][0] = dp[i][j][1] + dp[i][j][2]
答案为 dp[n][m][0]
#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 = + ;
const int mod = ;
ll dp[MAXN][MAXN][];
int n, m; int main()
{
scanf("%d%d", &n, &m);
REP(i, , ) dp[][][i] = ; _for(i, , n)
_for(j, , m)
{
if(i == && j == ) continue;
REP(k, , i)
dp[i][j][] = (dp[i][j][] + dp[k][j][] * dp[i-k][j][] % mod) % mod;
REP(k, , j)
dp[i][j][] = (dp[i][j][] + dp[i][k][] * dp[i][j-k][] % mod) % mod;
dp[i][j][] = (dp[i][j][] + dp[i][j][]) % mod;
} printf("%lld\n", dp[n][m][]); return ;
}

WZD的洞洞

WZD有一个形状为直角三角形的洞洞。

平面上有n个物品,每个物品有坐标xi,yi和价值wi,WZD希望用洞洞包含住价值尽可能高的物品。
由于洞洞的构造,其两条边必须平行于坐标轴,且直角在左下方(也就是说,洞洞只能平行移动),并且要求直角必须恰好落在一个物品上。
求洞洞能包含住最大价值为多少的物品。
40%的数据保证n≤5000;
100%的数据保证n≤100000;
100%的数据保证0<xi,yi≤10000,-10000<wi<10000。
Input
第一行一个整数n,表示有n个礼品。
第二行两个整数a,b,表示这个直角三角形的两个直角边的长度(a为平行于x轴的直角边的长度,b为平行y轴的直角边的长度)。
接下来n行,每行三个整数xi,yi,wi。
Output
仅一个数,表示最大价值。
Input示例
4
5 5
1 7 100
1 1 2
2 2 99
7 1 100
Output示例
101

这一题我判断点是不是在三角形内的方式中斜边是用数学中线性规划的方式判断的
完美的避开了正解
这道题的关键是一个三维偏序问题,但实际上是一个二维偏序问题
一个点被另外一个三角形包含有哪些条件?
三条边各一个条件
两条直角边就是对x和y坐标的限制
如果点j被点i的三角形覆盖
则有 xj >= xi, yj >= yi
这是很显然的
那么斜边呢
我开始是用线性规划的方法,推出斜边的方程,然后把点带进去如果小于0那就在斜边的下方
这么算没有错,但不能带来优化
我们要用一个比较麻烦却可以优化的方式来做
就是算出每个点到Bx + Ay = 0的距离(A,B如题目所述)
那么如果点j被点i的三角形覆盖
则有
dis[j] - dis[i] <= A * B / sqrt(A * A + B * B)
等号右边是三角形斜边上的高
这个式子可以拿笔画一下图,就懂了
那么我们可以用一个滑动窗口 O(n)的维护这个东西
先按照dis[i]从小到大排序
从大到小遍历,也就是逆序
每次把当前点的dis加入队列,这个时候队列里面每一个点的dis都是大于当前这个点的dis的
因为是按照从大到小加入的
那么最先加入的,也就是说现在处于队尾的,dis值就是最大的
那么可以判断拿队头(也就是新加入的点)和队尾做判断,看不满满足
dis[r] - dis[l] <= A * B / sqrt(A * A + B * B)
不满足就踢掉队尾,然后继续判断新的队尾,直到满足为止 dis的大小解决了,那么x和y呢??
我们可以观察出来,按照上述方式遍历,对于当前点i,和之前加入的点j
不可能同时满足xi > xj yi > yj
反证法。如果满足的话,那么有dis[i] > dis[j]
那么j这个点肯定还没有加入。
所以对于最坏只满足xi > xj yi > yj 其中一个
或者两个都不满足 那么我们把满足其中一个的点的权值去掉。
由于两个条件不会有交集
用两个树状数组维护就好了 然后有一些细节要注意,见注释
#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 = 1e5 + ;
const int MAXM = 1e4;
struct node
{
int x, y, w;
double dis;
bool operator < (const node& rhs) const
{
return dis < rhs.dis;
}
}s[MAXN];
int q[MAXN], n, A, B; struct Binary_Index_Tree
{
int f[(MAXN << ) + ]; Disjoint_Set() { memset(f, , sizeof(f)); } inline int lowbit(int x) { return x & (-x); } void add(int x, int p)
{
x += MAXN + ; //如果下标有负数,在这里改一下就好了,使得下标大于等于1,下标最大要改,数组空间要改
for(; x <= (MAXN << ) + ; x += lowbit(x))
f[x] += p;
} int sum(int x)
{
int res = ;
x += MAXN + ;
for(; x; x -= lowbit(x))
res += f[x];
return res;
}
}X, Y; int main()
{
scanf("%d%d%d", &n, &A, &B);
REP(i, , n)
{
scanf("%d%d%d", &s[i].x, &s[i].y, &s[i].w);
s[i].dis = (B * s[i].x + A * s[i].y) / sqrt(A * A + B * B);
}
sort(s, s + n); double dmax = A * B / sqrt(A * A + B * B); //用double,int有误差
int ans = , sum = , l = , r = ; //注意这里l与r的取值 for(int i = n - ; i >= ; i--)
{
sum += s[i].w;
q[++r] = i;
while(l <= r && s[q[l]].dis - s[q[r]].dis > dmax) //滑动窗口
{
sum -= s[q[l]].w;
X.add(s[q[l]].x, -s[q[l]].w);
Y.add(s[q[l]].y, -s[q[l]].w);
l++;
}
ans = max(ans, sum - X.sum(s[i].x - ) - Y.sum(s[i].y - )); //一定要区分小于和小于等于,小于记得要-1
X.add(s[i].x, s[i].w); Y.add(s[i].y, s[i].w); //做完了之后再把当前点加进去
} printf("%d\n", ans); return ;
}

总结

(1)只要复杂度是对的,直接枚举答案,不一定要二分。

(2)有子问题结构想dp。不重复的思想

(3)三维偏序问题化为二维偏序问题,二维偏序用树状数组为维护



NOIP2018提高组省一冲奖班模测训练(三)的更多相关文章

  1. NOIP2018提高组省一冲奖班模测训练(六)

    NOIP2018提高组省一冲奖班模测训练(六) https://www.51nod.com/Contest/ContestDescription.html#!#contestId=80 20分钟AC掉 ...

  2. NOIP2018提高组省一冲奖班模测训练(五)

    NOIP2018提高组省一冲奖班模测训练(五) http://www.51nod.com/Contest/ContestDescription.html#!#contestId=79 今天有点浪…… ...

  3. NOIP2018提高组省一冲奖班模测训练(四)

    NOIP2018提高组省一冲奖班模测训练(四) 这次比赛只AC了第一题,而且花了40多分钟,貌似是A掉第一题里面最晚的 而且还有一个半小时我就放弃了…… 下次即使想不出也要坚持到最后 第二题没思路 第 ...

  4. NOIP2018提高组省一冲奖班模测训练(二)

    比赛链接 NOIP2018提高组省一冲奖班模测训练(二) 今天发挥正常,昨天不在状态…… 花了很久A了第一题 第二题打了30分暴力 第三题投机取巧输出test1答案(连暴力都不知道怎么打,太弱了) 2 ...

  5. NOIP2018提高组省一冲奖班模测训练(一)

    比赛链接 https://www.51nod.com/contest/problemList.html#!contestId=72&randomCode=147206 这次考试的题非常有质量 ...

  6. [51Nod]NOIP2018提高组省一冲奖班模测训练(三) 题解

    链接 A.Anan的派对 题意:Anan想举办一个派对.Anan的朋友总共有 n 人.第i个人如果参加派对会得到 \(c_i\) 的快乐值,除他自己外每多一个人参加他会减少 \(d_i\) 的快乐值. ...

  7. [51Nod]NOIP2018提高组省一冲奖班模测训练(四)翻车记+题解

    链接 下午5点的时候,突然想起来有这个比赛,看看还有一个小时,打算来AK一下,结果因为最近智商越来越低,翻车了,我还是太菜了.上来10分钟先切掉了C和A,结果卡在了B题,唉. A.砍树 一眼题,两遍树 ...

  8. [51Nod]NOIP2018提高组省一冲奖班模测训练(二)

    http://www.51nod.com/contest/problemList.html#!contestId=73&randomCode=4408520896354389006 还是原题大 ...

  9. [51Nod]NOIP2018提高组省一冲奖班模测训练(一)题解

    http://www.51nod.com/contest/problemList.html#!contestId=72&randomCode=147206 原题水题大赛.. A.珂朵莉的旅行 ...

随机推荐

  1. js:Array对象常用方法介绍

    前言 在js中,数组作为一个特殊的对象.是我们常用的数据格式.今天就来梳理一下常用的数组方法. 1.基础 几种基础的就简单介绍一下:创建数组 var arr1 = new Array(); //括号可 ...

  2. TensorFlow 制作自己的TFRecord数据集

    官网的mnist和cifar10数据之后,笔者尝试着制作自己的数据集,并保存,读入,显示. TensorFlow可以支持cifar10的数据格式, 也提供了标准的TFRecord 格式,而关于 ten ...

  3. echarts在vue里面使用,以及基础配置。

    基础的图表和基础的配置. 效果图如下: 1.安装图表依赖包:npm install echarts 2.在main.js里面 引入echarts import echarts from 'echart ...

  4. Java基础学习总结(37)——Java23中设计模式(Design Patterns)详解

    设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  5. tomcat work目录的作用就是编译每个项目里的jsp文件为java文件如果项目没有jsp页面则这个项目文件夹为空

    最近发现,很多网友喜欢把tomcat的work目录里的东西叫做缓存,其实那不是很恰当,work目录只是tomcat的工作目录,也就是tomcat把jsp转换为class文件的工作目录,这也正是为什么它 ...

  6. shell脚本监测文件变化

    1. 我使用过的Linux命令之du - 查看文件的磁盘空间占用情况 用途说明 du命令是用来查看磁盘空间占用情况的,在Linux系统维护时常会用到,并且通常与df命令搭配使用.首先使用df看一下各个 ...

  7. MacBook Pro安装Photoshop且支持Retina有你们说的那么困难吗!

    直接看效果图! 超清晰吧...... 在此之前我也是网罗各种方法,各种步骤,各种琳琅满目.并且也没效果,要么是破解成功,要么是不支持Retina.这不瞎折腾嘛! 想起我在windows上的方法,认为在 ...

  8. js面向对象编程:怎样实现方法重载

    js中怎样实现方法重载?这涉及到三个问题 1同名函数的调用问题 2函数中特殊的參数arguments 3怎样利用arguments实现方法重载 1同名函数的调用问题 都知道在js中假设存在多个名称同样 ...

  9. Linux下FFmpeg的安装编译过程【转】

    本文转载自:http://www.linuxidc.com/Linux/2013-06/85628.htm 详细说下在Linux下FFmpeg的安装编译过程.参考 Ubuntu 10.04安装编译FF ...

  10. 机器学习 数据量不足问题----1 做好特征工程 2 不要用太多的特征 3 做好交叉验证 使用线性svm

    来自:https://www.zhihu.com/question/35649122 其实这里所说的数据量不足,可以换一种方式去理解:在维度高的情况下,数据相对少.举一个特例,比如只有一维,和1万个数 ...