线段树 (ICPC小米预赛第一场 EPhone Network )
Posted Kalzn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树 (ICPC小米预赛第一场 EPhone Network )相关的知识,希望对你有一定的参考价值。
题目链接
题意:给你一个1到n的数组。元素取值是[1, m]。对于1到m的每一个i。你要回答出至少包含[1,i]的所有元素各一次的最小区间长度。
题解:距离出线只差一题。队友在搞G题构造,我在搞这个。最后没出。没办法。一看题解,发现真的只差一步就搞出来了:vector存入1到m的每个数字的位置序列。想到了。线段数维护每个点作为左端点时的最近右端点。想到了。从[1,i]递推[1,i+1],因为这个数值单调可以二分寻找赋值。也想到了。最后一步通过最近右端点怎么维护最小区间长度,愣是没想到。很骚的是,题解也一笔带过了!而最骚的是,虽然题解没说怎么维护,但是看完题解不到10秒,我就知道该怎么维护了。 woc,真的魔幻啊。我自己都觉得我是个演员。
要说的是,我的这份代码是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的,因为在递推时寻找区间赋值是二分套线段树。很容易就可以想到多维护一个区间最近右端点的最大值,然后可以直接线段树 O ( l o g n ) O(logn) O(logn)查询,使得算法变成题解所说的 O ( n l o g n ) O(nlogn) O(nlogn)。但是1e5数据量,没有卡。本人图方便(懒)就直接二分上了。
下面是ac代码:
// % everyone
#include <cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <cctype>
#include <time.h>
namespace IO
double start_time = 0.0;
void ct() start_time = clock(); return;
void fast_cin() std::ios::sync_with_stdio(false); std::cin.tie();
void read_f(int flag = 0) freopen("0.in", "r", stdin); if(!flag) freopen("0.out", "w", stdout);
void run_time() std::cout << "\\nESC in : " << ( clock() - start_time ) * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl;
using namespace IO;
template <typename T>
bool bacmp(const T & a, const T & b) return a > b;
template <typename T>
bool pecmp(const T & a, const T & b) return a < b;
#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr make_pair
#define pb push_back
using namespace std;
const int N = 2e5+5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
struct Node
int l, r;
int val;
int ans;
int laz;
tr[N<<2];
inline void update(int p, int v)
tr[p].val = v;
tr[p].ans = v - tr[p].r + 1;
tr[p].laz = v;
inline void pushup(int p)
tr[p].val = min(tr[p<<1].val, tr[p<<1|1].val);
tr[p].ans = min(tr[p<<1].ans, tr[p<<1|1].ans);
inline void spread(int p)
if (tr[p].laz == 0) return;
update(p<<1, tr[p].laz);
update(p<<1|1, tr[p].laz);
tr[p].laz = 0;
void build(int p, int l, int r)
tr[p].l = l; tr[p].r = r;
tr[p].ans = tr[p].laz = tr[p].val = 0;
if(l == r)
return;
int mid = (l + r) >> 1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
pushup(p);
void change(int p, int l, int r, int v)
if (l > r) return;
if (l <= tr[p].l && tr[p].r <= r)
update(p, v);
return;
spread(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if (l <= mid) change(p<<1, l, r, v);
if (r > mid) change(p<<1|1, l, r, v);
pushup(p);
return;
int ask(int p, int x)
if (tr[p].l == tr[p].r) return tr[p].val;
int mid = (tr[p].l + tr[p].r) >> 1;
spread(p);
if (x <= mid) return ask(p<<1, x);
else return ask(p<<1|1, x);
vector<int> num[N];
int che(int l, int r)
int gg = r;
while(l <= r)
int mid = (l + r) >> 1;
if (ask(1, mid) < gg) l = mid+1;
else r = mid-1;
return l-1;
int main()
int n, m;
cin >> n >> m;
build(1, 1, n);
for (int i = 1; i <= m; i++) num[i].pb(0);
for (int i = 1; i <= n; i++)
int te; scanf("%d", &te);
num[te].pb(i);
for (int i = 1; i <= m; i++)
// cout << i << " ::: " << endl;
for (int j = 1; j < num[i].size(); j++)
int p = che(num[i][j-1]+1, num[i][j]);
change(1, num[i][j-1]+1, p, num[i][j]);
change(1, *num[i].rbegin()+1, n, inf);
// for (int i = 1; i <= n; i++)
//
// cout << ask(1, i) << " ";
//
// cout << endl;
printf("%d ", tr[1].ans);
return 0;
以上是关于线段树 (ICPC小米预赛第一场 EPhone Network )的主要内容,如果未能解决你的问题,请参考以下文章
ACM-ICPC 2018 南京赛区网络预赛 Lpl and Energy-saving Lamps 线段树
ACM-ICPC 2018 南京赛区网络预赛 G Lpl and Energy-saving Lamps(线段树)
[计蒜客] ACM-ICPC 2018 南京赛区网络预赛 | 部分题解 | 线段树 + 线性筛 + 最短路
ACM-ICPC 2018 徐州赛区网络预赛 H. Ryuji doesn't want to study (线段树)
ACM-ICPC 2018 南京赛区网络预赛 G. Lpl and Energy-saving Lamps (弱线段树)