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. VS调试ASP.NET浏览器会不断的发出POLL请求

    Poll管道不断发送请求问题! Visual Studio 2013的一个问题Visual Studio 2013中在IE浏览器浏览localhost网站时候,发现会不断有下面链接的请求,这个是怎么回 ...

  2. Vijos—— T 1359 Superprime

    https://vijos.org/p/1359 描述 农民约翰的母牛总是生产出最好的肋骨.你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们. 农民约翰确定他卖给买方的是真正的质数肋骨,是因 ...

  3. rabbitMQ学习笔记(四) 发布/订阅消息

    前面都是一条消息只会被一个消费者处理. 如果要每个消费者都处理同一个消息,rabbitMq也提供了相应的方法. 在以前的程序中,不管是生产者端还是消费者端都必须知道一个指定的QueueName才能发送 ...

  4. Qt Quick 之 QML 与 C++ 混合编程具体解释

    Qt Quick 技术的引入.使得你能够高速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的.也有非常多局限性,原来 Qt 的一些技术,比方低阶的网络编程如 QTcpSocke ...

  5. 【机房重构】—上机&amp;订餐

    前几天通过UML图中的时序图.让我对于机房重构中的每一条线理解的更加清晰.曾经认为上机特别的乱,在一次偶遇中,得知了原来它能够转化成我们平时订餐.以下就听我说一说上机&订餐的故事吧! 又是发生 ...

  6. MySql 基础学习笔记 1——概述与基本数据类型: 整型: 1)TINYINT 2)SMALLINT 3) MEDIUMINT 4)INT 5)BIGINT 主要是大小的差别 图 浮点型:命令

    一.CMD中经常使用mysql相关命令 mysql -D, --database=name  //打开数据库 --delimiter=name  //指定分隔符 -h, --host=name  // ...

  7. 通过Gulp流方式处理流程

    http://www.cnblogs.com/gongcheng9990/archive/2014/11/25/4120434.html http://modernweb.com/2014/08/04 ...

  8. ES内存持续上升问题定位

      https://discuss.elastic.co/t/memory-usage-of-the-machine-with-es-is-continuously-increasing/23537/ ...

  9. Oracle Hint的用法

    1. /*+ALL_ROWS*/ 表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化. 例如: SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_I ...

  10. docker compose线下安装

    Compose 是一个用户定义和运行多个容器的 Docker 应用程序.在 Compose 中你可以使用 YAML 文件来配置你的应用服务.然后,只需要一个简单的命令,就可以创建并启动你配置的所有服务 ...