NOIP2018提高组省一冲奖班模测训练(四)
NOIP2018提高组省一冲奖班模测训练(四)
这次比赛只AC了第一题,而且花了40多分钟,貌似是A掉第一题里面最晚的
而且还有一个半小时我就放弃了……
下次即使想不出也要坚持到最后
第二题没思路
第三题想用分块搞一搞,然后发现空间开不下(其实可以用分块搞)
主要是认识了今天AK的曲恒毅大佬,收获很大
砍树
比如这个例子中,树上有5个白节点,3个黑节点,奇怪值就是|3-5|=2。
小D想让你在原树中砍出来一个连通块,使得这个连通块的奇怪值最大。(注意连通块当然也会是棵树。)
首先输入n。
接下来一行n个数c_1,c_2,…,c_n 。若c_i=0,表示节点i颜色为白色,否则为黑色。
接下来n-1行,每行两个数u,v,描述树上一条连接节点u,v的边。
对于20%的数据,n≤20
对于40%的数据,n≤100
对于60%的数据,n≤1000
对于100%的数据,n≤10^5,1≤x,y≤n,c_i∈{0,1}
输出一行,表示最大的奇怪值。
8
1 0 0 1 1 0 0 0
7 1
3 5
1 6
4 3
6 3
2 3
7 8
4
看完第一反应树形dp
但是感觉这个绝对值不好处理,同时不知道怎么求这个连通块
然后绕了半天突然想到可以绝对值可以分开处理
求连通块的话更新的时候贪心一下,只加入dp值>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; const int MAXN = 1e5 + ;
struct Edge{ int to, next; };
Edge e[MAXN << ];
int head[MAXN], tot;
int c[MAXN], d[MAXN], dp[MAXN], n; void AddEdge(int from, int to)
{
e[tot] = Edge{to, head[from]};
head[from] = tot++;
} void dfs(int u, int fa)
{
dp[u] = d[u];
for(int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
if(dp[v] > ) dp[u] += dp[v];
}
} int main()
{
memset(head, -, sizeof(head)); tot = ;
scanf("%d", &n);
_for(i, , n) scanf("%d", &c[i]);
REP(i, , n)
{
int u, v;
scanf("%d%d", &u, &v);
AddEdge(u, v); AddEdge(v, u);
} int ans = ;
_for(i, , n) d[i] = c[i] ? : -;
dfs(, -);
_for(i, , n) ans = max(ans, dp[i]); _for(i, , n) d[i] = c[i] ? - : ;
dfs(, -);
_for(i, , n) ans = max(ans, dp[i]); printf("%d\n", ans); return ;
}
奇怪的回文串
现在小D有一个长度为N的字符串S。他有K次修改这个字符串的机会。每次修改,他可以选择字符串上的任意一个位置,把这个位置修改成任意一种字符。他希望使得修改后的串中满足“奇数-回文”性质的子串的长度最大。注意K次机会不必用完。
第一行包含两个整数K,N。
接下来一行N个整数,第i个整数代表字符串S第i位的字符。
对于20%的数据,N≤10
对于40%的数据,N≤100
对于60%的数据,N≤5000
对于100%的数据,1≤K≤N≤500000,保证给定的字符串的字符为1到10^9间的整数。
输出一个整数,表示最大可能的满足“奇数-回文”性质的子串长度。
1 6
1 2 3 4 5 6
3
这道题没怎么深入思考
首先要看出一个结论
如果所有奇数子串都是回文串,等价于所有长度为3的字串都为回文串 所有长度为3的字串都为回文串等价于奇数位置字符同,偶数位置字符同
这个结论画画图就可以发现 那么我们显然可以枚举子串,然后分别算出偶数位置和奇数位置同一个字符最多有多少个
如果分别由w0, w1个
那么就需要修改
序列长度-w0-w1次
那么问题就怎么高效地维护这个最大值
我们可以用一个滑动窗口来做
固定左端点,每次右端点在次数小于k的情况下尽量拓展,用序列长度更新答案
然后左端点加加,重复上个过程
在其中写两个支持删除操作的堆,维护奇数位置和偶数位置中同一个字符最多有多少个
支持删除操作的堆可以用堆优化dijsktra算法中的堆的思路 右端点共右移n次,左端点同样,所以滑动窗口的复杂度是O(n)的
然后每次操作维护堆是logn的
总复杂度是O(nlogn)的
然后我惊奇的发现用unorderer_map和手写离散化跑出来的时间基本相同。
那以后就可以用unordered_map离散化了,不需要手写了
#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 = 5e5 + ;
int a[MAXN], n, k; unordered_map<int, int> cnt[];
struct node
{
int id, w;
bool operator < (const node& rhs) const
{
return w < rhs.w;
}
};
priority_queue<node> q[]; int main()
{
scanf("%d%d", &k, &n);
_for(i, , n) scanf("%d", &a[i]); int l = , r = , ans = ;
_for(i, , n)
{ while(r <= n)
{
if(r & ) q[].push(node{a[r], ++cnt[][a[r++]]});
else q[].push(node{a[r], ++cnt[][a[r++]]}); node t1, t0;
while(!q[].empty())
{
t1 = q[].top();
if(cnt[][t1.id] == t1.w) break;
q[].pop();
}
while(!q[].empty())
{
t0 = q[].top();
if(cnt[][t0.id] == t0.w) break;
q[].pop();
} if(r - l - t1.w - t0.w > k) break;
ans = max(ans, r - l);
} if(l & ) q[].push(node{a[l], --cnt[][a[l++]]});
else q[].push(node{a[l], --cnt[][a[l++]]});
} printf("%d\n", ans); return ;
}
范围查询
1.left≤i≤right
2.a_i≡y(mod x)
其中a_i≡y(mod x)表示,a_i对x取模的值为y。
首先输入两个数n,q。
接下来一行n个数a_0,a_1,…,a_(n-1) 。
接下来q行,每行四个数left,right,x,y,描述一个询问。
总共有10个数据点。
对于第1,2个数据点,n,q≤1000
对于第3,4,5,6个数据点,x≤1000
对于100%的数据,1≤n,q≤40000,0≤a_i≤40000,1≤x≤40000,0≤y<x,0≤left≤right<n。
输出q行,表示每个询问对应的答案。
5 3
250 501 5000 5 4
0 4 5 0
0 4 10 0
0 4 3 2
3
2
2
这道题曲恒毅大佬用莫队跑过去了,他说因为数据比较水
我学习了一下他的写法,自己写了一遍,练习一波莫队
这道题是问l到r中符合a_i≡y(mod x)的有多少个
直观的想法是枚举每一个a_i看满不满足
但我们可以逆向思维
枚举所有 mod x等于y的数,看这些数在l到r中出现了几次。
所以可以用莫队维护一个桶
不过这个算法显然可以卡掉,当很多x非常小而最大x非常大的时候就要遍历很多次
但是这题的数据是随机的比较水。
#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 = 4e4 + ;
int bl[MAXN], ans[MAXN], a[MAXN];
int f[MAXN], n, m, blo, maxt;
struct node
{
int l, r, x, y, id;
bool operator < (const node& rhs) const
{
return bl[l] < bl[rhs.l] || bl[l] == bl[rhs.l] && r < rhs.r;
}
}q[MAXN]; int main()
{
scanf("%d%d", &n, &m);
blo = sqrt(n); _for(i, , n)
{
scanf("%d", &a[i]);
maxt = max(maxt, a[i]);
bl[i] = i / blo;
} _for(i, , m)
{
scanf("%d%d%d%d", &q[i].l, &q[i].r, &q[i].x, &q[i].y);
q[i].l++; q[i].r++;
q[i].id = i;
} sort(q + , q + m + ); int l = , r = ;
_for(i, , m)
{
while(r < q[i].r) ++f[a[++r]];
while(l > q[i].l) ++f[a[--l]];
while(r > q[i].r) --f[a[r--]];
while(l < q[i].l) --f[a[l++]];
for(int j = q[i].y; j <= maxt; j += q[i].x)
ans[q[i].id] += f[j];
} _for(i, , m) printf("%d\n", ans[i]); return ;
}
讲一下正解,写了好久好久才AC,而且发现最慢的点984ms,还不如写莫队时间快
顺便吐糟一下数据有问题,竟然有x=0的,题目中给的是x>0的,我交上去RE,调试了好久才发现是数据本身的问题
正解包含了x<=1000的解法, 所以先讲x比较小的情况
对于x<=1000的时候,而询问有40000,这意味着有很多很多的x是重复的,平均下来重复40个x
那么这种情况下就启发我们可以把x相同的一起处理
那么我们就按照x排序,把x相同的一起处理
对于每个询问,我们可以打标机,在l-1处打上id, y, -1的标记,在r处打上id, y, 1的标记, id是这个询问在题目输入中是第几个,方便统计答案
有什么用呢?
打完标机后我们可以从1到n扫一遍数组,同时用一个cnt记录每个数模x等于y中y有多少次
那么遇到标记就可以使 ans[id] += cnt[y] * p;
这个p有什么用?对于区间[l, r], 可以转化为[1,r] - [1, l - 1]
这个p就是维护这个正负的
同时注意一个位置可能有很多个标记,所以要用一个vector存
复杂度的话,有xmax组(xmax表示最大的x),每组要扫一遍数组O(n)
所以复杂度是O(nxmax),和最大的x有关。
40000*1000其实很极限了,所以有一个点是9秒多卡过去的
如果最大的x比较小,就用上述方法。然后我们考虑x比较大的情况
这时候的解法和莫队的写法有一点点相似之处
莫队的写法使用与x比较大的情况,所以去枚举所有modx=y的数不会太慢
而这道题也因为x比较大,所以也去枚举modx=y的数
那么对于枚举出的每一个数,怎么迅速这个数在[l,r]中存在多少次?
不知道大家有没有做过区间众数那道题,那道题也存在上一行的问题
我们可以把数组中每一数,以值作为下标,位置作为值扔到一个vector中
比如对于5的数,在3 4 6 7这4个位置中出现过
那么如果询问在位置3到5中存在几个5
那么首先可以二分出左边界,也就是找>=3的第一个数是的位置
因为在3 4 6 7这4个位置中出现过,所以这个数显然是3,位置是1(3 4 6 7的下标从1开始,从0开始也可以,反正求的是差值)
然后可以二分出右边界+1,也就是找出大于(没有等于)5的第一个数是多少
因为在3 4 6 7这4个位置中出现过,这个数显然是6,位置是3
那么答案就是右边界+1-左边界,也就是3 - 1 = 2
我们验证一下,在3 4 6 7中3到5有3和4两个地方
所以答案是对的
可能看一遍看不懂,多看几遍,注意哪个是下标,哪个是值
如果设最大的数组中的值为M
那么这个复杂度是O(Q * M / xmin * logn)
那么现在存在一个问题,到底以x为多少分界线是最优的,这个时候我们可以用类似算分块大小中每一块多大的的方法来算
总复杂度是O(Q * M / xmin * logn +n*xmax)
这里的xmax和xmin就是我们要求的分界值,设为L
那么O(Q * M / L * logn +n*L)
这里Q和n都是小于等于40000,是同阶的,可以都看作Q
那么Q提出来
O( Q(M / L * logn +L))
那么由均值不等式知
当M / L * logn =L时,它们相乘最小
可得L = sqrt(M * logn)
最后提一下我代码中用了unordered_map
如果本地没有加c++11的编译命令的话会CE(怎么加自行百度)
提交代码的时候也要注意选C++11, 选C++会CE
#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 = 4e4 + ;
struct node
{
int l, r, x, y, id;
bool operator < (const node& rhs) const
{
return x < rhs.x;
}
}q1[MAXN], q2[MAXN];
int a[MAXN], cnt1, cnt2;
int M, n, m, L, ans[MAXN]; struct flag
{
int id, y, p;
};
vector<flag> t[MAXN];
unordered_map<int, int> cnt; void solve1()
{
sort(q1 + , q1 + cnt1 + );
int i = ;
while(i <= cnt1)
{
cnt.clear();
int j = i - ;
while(j + <= cnt1 && q1[j+].x == q1[i].x)
{
j++;
t[q1[j].l - ].push_back(flag{q1[j].id, q1[j].y, -});
t[q1[j].r].push_back(flag{q1[j].id, q1[j].y, });
} _for(k, , n)
{
cnt[a[k] % q1[i].x]++;
if(t[k].size())
{
REP(r, , t[k].size())
ans[t[k][r].id] += cnt[t[k][r].y] * t[k][r].p;
t[k].clear();
}
}
i = j + ;
}
} vector<int> g[MAXN]; void solve2()
{
_for(i, , n) g[a[i]].push_back(i);
_for(i, , M) sort(g[i].begin(), g[i].end());
_for(i, , cnt2)
for(int j = q2[i].y; j <= M; j += q2[i].x)
{
int L = lower_bound(g[j].begin(), g[j].end(), q2[i].l) - g[j].begin();
int R = upper_bound(g[j].begin(), g[j].end(), q2[i].r) - g[j].begin();
ans[q2[i].id] += R - L;
}
} int main()
{
scanf("%d%d", &n, &m);
_for(i, , n)
{
scanf("%d", &a[i]);
M = max(M, a[i]);
} L = sqrt(M * log2(n)); _for(i, , m)
{
node t;
scanf("%d%d%d%d", &t.l, &t.r, &t.x, &t.y); t.l++, t.r++;
if(t.x == ) continue; //好坑!!
if(t.x < L) q1[++cnt1] = t, q1[cnt1].id = i;
else q2[++cnt2] = t, q2[cnt2].id = i;
} solve1();
solve2();
_for(i, , m) printf("%d\n", ans[i]); return ;
}
总结
(1)认识大佬
(2)字符串观察结论,写带删除的堆
(3)位置二分,逆向思维,莫队,合并处理
NOIP2018提高组省一冲奖班模测训练(四)的更多相关文章
- NOIP2018提高组省一冲奖班模测训练(六)
NOIP2018提高组省一冲奖班模测训练(六) https://www.51nod.com/Contest/ContestDescription.html#!#contestId=80 20分钟AC掉 ...
- NOIP2018提高组省一冲奖班模测训练(五)
NOIP2018提高组省一冲奖班模测训练(五) http://www.51nod.com/Contest/ContestDescription.html#!#contestId=79 今天有点浪…… ...
- NOIP2018提高组省一冲奖班模测训练(三)
NOIP2018提高组省一冲奖班模测训练(三) 自己按照noip的方式考,只在最后一两分钟交了一次 第一题过了,对拍拍到尾. 第二题不会.考试时往组合计数的方向想,推公式,推了一个多小时,大脑爆炸,还 ...
- NOIP2018提高组省一冲奖班模测训练(二)
比赛链接 NOIP2018提高组省一冲奖班模测训练(二) 今天发挥正常,昨天不在状态…… 花了很久A了第一题 第二题打了30分暴力 第三题投机取巧输出test1答案(连暴力都不知道怎么打,太弱了) 2 ...
- NOIP2018提高组省一冲奖班模测训练(一)
比赛链接 https://www.51nod.com/contest/problemList.html#!contestId=72&randomCode=147206 这次考试的题非常有质量 ...
- [51Nod]NOIP2018提高组省一冲奖班模测训练(三) 题解
链接 A.Anan的派对 题意:Anan想举办一个派对.Anan的朋友总共有 n 人.第i个人如果参加派对会得到 \(c_i\) 的快乐值,除他自己外每多一个人参加他会减少 \(d_i\) 的快乐值. ...
- [51Nod]NOIP2018提高组省一冲奖班模测训练(四)翻车记+题解
链接 下午5点的时候,突然想起来有这个比赛,看看还有一个小时,打算来AK一下,结果因为最近智商越来越低,翻车了,我还是太菜了.上来10分钟先切掉了C和A,结果卡在了B题,唉. A.砍树 一眼题,两遍树 ...
- [51Nod]NOIP2018提高组省一冲奖班模测训练(二)
http://www.51nod.com/contest/problemList.html#!contestId=73&randomCode=4408520896354389006 还是原题大 ...
- [51Nod]NOIP2018提高组省一冲奖班模测训练(一)题解
http://www.51nod.com/contest/problemList.html#!contestId=72&randomCode=147206 原题水题大赛.. A.珂朵莉的旅行 ...
随机推荐
- Python学习笔记(1)Pycharm基本认识
Pycharm的使用 集成开发环境(IDE,集成开发环境) VIM #经典的linux下的文本编辑器 Pycharm,主要用于python开发的ide PyCharm常用快捷键 Ctrl + / # ...
- IIS部署ASP.NET网站后提示只有在配置文件或 Page 指令中将 enableSessionState 设置为 true 时,才能使用会话状态...
今天,在IIS上部署网站后,出现了下面错误: 只有在配置文件或 Page 指令中将 enableSessionState 设置为 true 时,才能使用会话状态.还请确保在应用程序配置的 <sy ...
- Java 接口技术 Interface
一.什么是接口技术(Interface): //举例中Comparable是一个接口,Employee是一个类 1.接口不是类,而是对类的一组描述,并不给出每个类的具体实现. 2.一个类可以实现多个接 ...
- SpringBoot 对静态资源的映射规则
一.所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源 webjars:以jar包的方式引入静态资源,如下:引入 jquery ...
- CF 372B Counting Rectangles is Fun [dp+数据维护]
题意,给出一个n行m列的矩阵 里面元素是0或者1 给出q个询问 a,b,c,d 求(a,b)到(c,d)有多少个由0组成的矩形 我们定义 watermark/2/text/aHR0cDovL2Jsb2 ...
- 石子合并(区间dp)
石子合并(一) 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描写叙述 有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程仅仅能每次将相邻 ...
- mysql事务的开启
mysql事务的开启 对于一个MYSQL数据库(InnoDB),事务的开启与提交模式无非下面这两种情况: 1>若参数autocommit=0,事务则在用户本次对数据进行操作时自动开启,在用户执行 ...
- oracle 11gR2 如何修改scan vip 地址 /etc/hosts方式
这次帮客户搭建了一套oracle 11gR2 rac for aix环境,scan vip因为网络调整需要,需要更改以前设置好的scan vip,是采用/etc/hosts的方式,比如将scan vi ...
- 关于QObject类的一些理解
QRunnable并没有继承自QObject,所以它和其他QObject组件的通信不能使用传统的信号和槽,要是用信号和槽我们必须将其继承自QObject自动的添加 QThread的退出最好用exit( ...
- 42.写入XML
#include <QtGui> #include <QtXml> #include <iostream> //创建一个树结构 void populateTree( ...