可以推出

min[i]要么是i要么是1,当a序列中存在这个数是1

max[i]的话就比较麻烦了

首先对于i来说,如果还没有被提到第一位的话,他的max可由他后面的这部分序列中 j>=i 的不同数多少所决定,这个可以用树状数组解决

其次就是两次被提到第一位的中间的空当,这个空当中不同的数的大小,也会决定max,这里的解法比较多样,我用的是主席树

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#define MP make_pair
#define ll long long
#define ld long double
#define null NULL
#define all(a) a.begin(), a.end()
#define forn(i, n) for (int i = 0; i < n; ++i)
#define sz(a) (int)a.size()
// #define lson l , m , rt << 1
// #define rson m + 1 , r , rt << 1 | 1
#define bitCount(a) __builtin_popcount(a)
template<class T> int gmax(T &a, T b) { if (b > a) { a = b; return 1; } return 0; }
template<class T> int gmin(T &a, T b) { if (b < a) { a = b; return 1; } return 0; }
using namespace std;
const int INF = 0x3f3f3f3f;
string to_string(string s) { return '"' + s + '"'; }
string to_string(const char* s) { return to_string((string) s); }
string to_string(bool b) { return (b ? "true" : "false"); }
template <typename A, typename B>
string to_string(pair<A, B> p) { return "(" + to_string(p.first) + ", " + to_string(p.second) + ")"; }
template <typename A>
string to_string(A v) { bool first = true; string res = "{"; for (const auto &x : v) { if (!first) { res += ", "; } first = false; res += to_string(x); } res += "}"; return res; }
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) { cerr << " " << to_string(H); debug_out(T...); }
#ifdef LOCAL
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif // const int MAXN = 3e5 + 5;
// const int M = MAXN * 100;
int n, m;
vector<int> T, lson, rson, c;
int tot = 0; int newNode() {
lson.push_back(0); rson.push_back(0); c.push_back(0);
return tot ++;
}
int build(int l, int r) {
int root = newNode();
c[root] = 0;
if(l != r) {
int mid = (l + r) >> 1;
lson[root] = build(l, mid);
rson[root] = build(mid + 1, r);
}
return root;
} int update(int root, int pos, int val) {
int newroot = newNode(), tmp = newroot;
c[newroot] = c[root] + val;
int l = 1, r = n;
while(l < r) {
int mid = (l + r) >> 1;
if(pos <= mid) {
lson[newroot] = newNode(); rson[newroot] = rson[root];
newroot = lson[newroot]; root = lson[root];
r = mid;
} else {
rson[newroot] = newNode(); lson[newroot] = lson[root];
newroot = rson[newroot]; root = rson[root];
l = mid + 1;
}
c[newroot] = c[root] + val;
}
return tmp;
} int query(int root, int pos) {
int ret = 0;
int l = 1, r = n;
while(pos < r) {
int mid = (l + r) >> 1;
if(pos <= mid) {
r = mid;
root = lson[root];
} else {
ret += c[lson[root]];
root = rson[root];
l = mid + 1;
}
}
return ret + c[root];
} class BIT {
private:
vector<int> tree;
int treesize;
public:
BIT(int x) {
tree.resize(x + 5, 0);
treesize = x + 5;
} int Sum(int x) {
if(x <= 0) return 0;
if(x > treesize) x = treesize;
int ans = 0;
while(x > 0) {
ans += tree[x];
x -= x & -x;
}
return ans;
}
void Add(int x, int d) {
// debug(x);
while(x <= treesize) {
tree[x] += d;
x += x & -x;
}
}
}; int main() { while(~scanf("%d %d", &n, &m)) {
vector<int> vc;
vector<int> mp(max(n,m) + 5, 0);
vector<int> maxx(n + 5, 0);
vector<int> minn(n + 5, 0);
vector<vector<int> > E(n + 5, vector<int>());
for(int i = 1; i <= n; ++i) {
maxx[i] = minn[i] = i;
} for(int i = 0; i < m; ++i) {
int t; scanf("%d", &t);
vc.push_back(t);
E[t].push_back(i + 1);
minn[t] = 1;
} BIT bit = BIT(n + 5);
// set<int> st;
for(int i = 0; i < m; ++i) {
if(mp[vc[i]] == 0) {
bit.Add(vc[i], 1);
maxx[vc[i]] = max(maxx[vc[i]], vc[i] + bit.Sum(n) - bit.Sum(vc[i]));
debug(i, vc[i], maxx[vc[i]]);
}
mp[vc[i]] = 1;
} for(int i = 1; i <= n; ++i) {
if(mp[i] == 0) {
maxx[i] = max(maxx[i], i + bit.Sum(n) - bit.Sum(i));
// debug(i, maxx[i]);
}
} tot = 0;
T.clear();
for(int i = 0; i < m + 5; ++i) T.push_back(i);
T[m + 1] = build(1, m);
for(int i = 1; i <= n; ++i) mp[i] = 0;
for(int i = m; i >= 1; -- i) {
int target = vc[i-1];
if(mp[target] == 0) {
T[i] = update(T[i + 1], i, 1);
} else {
int tmp = update(T[i + 1], mp[target], -1);
T[i] = update(tmp, i, 1);
}
mp[target] = i;
} debug(query(T[m + 1], m + 1), query(T[m + 1], m)); for(int i = 1; i <= n; ++i) {
int pre = m + 1;
for(int j = E[i].size() - 1; j >= 0; --j) {
int tmp = query(T[E[i][j]], pre - 1);
maxx[i] = max(maxx[i], tmp);
// debug(i, E[i][j] + 1, pre - 1, tmp);
pre = E[i][j];
}
} for(int i = 1; i <= n; ++i) {
printf("%d %d\n", minn[i], maxx[i]);
}
}
return 0;
}

官方给出了一种比较新颖的直接线段树的做法,我觉得写的非常有趣,

#include <bits/stdc++.h>

#define forn(i, n) for (int i = 0; i < int(n); i++)
#define x first
#define y second using namespace std; const int N = 300 * 1000 + 13; typedef pair<int, int> pt; int n;
int a[N];
vector<int> pos[N];
pt ans[N];
int prv[N]; vector<int> t[4 * N]; void build(int v, int l, int r){
if (l == r - 1){
t[v].push_back(prv[l]);
return;
}
int m = (l + r) / 2;
build(v * 2, l, m);
build(v * 2 + 1, m, r);
t[v].resize(r - l);
merge(t[v * 2].begin(), t[v * 2].end(), t[v * 2 + 1].begin(), t[v * 2 + 1].end(), t[v].begin());
} int get(int v, int l, int r, int L, int R, int val){
if (L >= R)
return 0;
if (l == L && r == R)
return lower_bound(t[v].begin(), t[v].end(), val) - t[v].begin();
int m = (l + r) / 2;
return get(v * 2, l, m, L, min(m, R), val) + get(v * 2 + 1, m, r, max(m, L), R, val);
} int f[N]; void upd(int x){
for (int i = x; i >= 0; i = (i & (i + 1)) - 1)
++f[i];
} int get(int x){
int res = 0;
for (int i = x; i < N; i |= i + 1)
res += f[i];
return res;
} int main() {
int n, m;
scanf("%d%d", &n, &m);
forn(i, m){
scanf("%d", &a[i]);
--a[i];
}
forn(i, m){
pos[a[i]].push_back(i);
} vector<pt> qr;
forn(i, n){
for (int j = 1; j < int(pos[i].size()); ++j)
qr.push_back(make_pair(pos[i][j - 1] + 1, pos[i][j] - 1));
if (!pos[i].empty())
qr.push_back(make_pair(pos[i].back() + 1, m - 1));
} forn(i, n) ans[i] = {i, i};
forn(i, m) ans[a[i]].x = 0; forn(i, n){
int cur = -1;
for (auto it : pos[i]){
prv[it] = cur;
cur = it;
}
}
build(1, 0, m); forn(i, qr.size()){
int l = qr[i].x;
int r = qr[i].y;
if (r < l) continue;
int x = a[qr[i].x - 1];
int cnt = get(1, 0, m, l, r + 1, l);
ans[x].y = max(ans[x].y, cnt);
} forn(i, m){
if (i == pos[a[i]][0]){
ans[a[i]].y = max(ans[a[i]].y, a[i] + get(a[i]));
upd(a[i]);
}
}
forn(i, n) if (pos[i].empty()){
ans[i].y = max(ans[i].y, i + get(i));
} forn(i, n) printf("%d %d\n", ans[i].x + 1, ans[i].y + 1);
return 0;
}

Educational Codeforces Round 80 (Rated for Div. 2) E. Messenger Simulator的更多相关文章

  1. Educational Codeforces Round 80 (Rated for Div. 2)

    A. Deadline 题目链接:https://codeforces.com/contest/1288/problem/A 题意: 给你一个 N 和 D,问是否存在一个 X , 使得 $x+\lce ...

  2. Educational Codeforces Round 80 (Rated for Div. 2)D E

    D枚举子集 题:https://codeforces.com/contest/1288/problem/D题意:给定n个序列,每个序列m个数,求第i个和第j个序列组成b序列,b序列=max(a[i][ ...

  3. Educational Codeforces Round 80 (Rated for Div. 2)部分题解

    A. Deadline 题目链接 题目大意 给你\(n,d\)两个数,问是否存在\(x\)使得\(x+\frac{d}{x+1}\leq n\),其中\(\frac{d}{x+1}\)向上取整. 解题 ...

  4. Educational Codeforces Round 80 (Rated for Div. 2)(A-E)

    C D E 这三道题感觉挺好       决定程序是否能通过优化在要求的时间内完成,程序运行时间为t,你可以选择花X天来优化,优化后程序的运行时间为t/(x+1)取上整,花费的时间为程序运行时间加上优 ...

  5. Educational Codeforces Round 80 (Rated for Div. 2)E(树状数组,模拟,思维)

    #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; ],mx[],a[],pos[],sum ...

  6. Educational Codeforces Round 80 (Rated for Div. 2)D(二分答案,状压检验)

    这题1<<M为255,可以logN二分答案后,N*M扫一遍表把N行数据转化为一个小于等于255的数字,再255^2检验答案(比扫一遍表复杂度低),复杂度约为N*M*logN #define ...

  7. Educational Codeforces Round 80 (Rated for Div. 2)C(DP)

    #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; ; ][],temp[][]; int ...

  8. Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship Time Limit: 2000 mSec P ...

  9. Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems(动态规划+矩阵快速幂)

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems Time Limit: 3000 mSec P ...

随机推荐

  1. FineReport报表和水晶报表的比较

    FineReport报表和水晶报表的比较 FineReport报表软件针对复杂格式的报表数据及Web报表的展现,通过多源分片.不规则分组.双向扩展来轻松拖拽做复杂格式的报表,制作报表从此摆脱了复杂的S ...

  2. 学习Java第五周

    通过这一段时间的学习发现Java和C++虽然都是面向对象的编程语言,有相似之处也有不同之处,相似的地方总会感觉易于接受,不同之处或者新接触的有些知识不是很好理解和掌握. 前一段时间学的内部类和接口便是 ...

  3. JMeter Web测试计划

    在本节中,将学习如何创建测试网页的基本测试计划. 出于演示测试目的,我们将测试URL - https://www.yiibai.com/ 的网页性能. 创建JMeter测试计划 进入到JMeter安装 ...

  4. 22.XML

    转载:https://www.cnblogs.com/yuanchenqi/article/5732581.html xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用 ...

  5. Spring Boot中路径及配置文件读取问题

    编译时src/main/java中*.java文件会被编译成*.class文件,在classpath中创建对应目录及class文件           src/main/resources目录中的文件 ...

  6. 20191121-3 Final阶段贡献分配规则

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/10063 ”组长”组final阶段贡献分分配规则 组里五位成员分别有入团队 ...

  7. The Annual Summary Of 2019

    Time is flying, it arrives at the end of year again. This is my first year working in PinDuoDuo inc ...

  8. 编译GLib C程序

    编译GLib C程序 GLib是GTK +所需的实用程序库,但也可以在非GUI应用程序中独立使用.本文介绍如何在Linux中编译使用GLib的C程序.它还显示了如何为系统上安装的GLib版本安装正确的 ...

  9. LibreOJ 6277. 数列分块入门 1 题解

    题目链接:https://loj.ac/problem/6277 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,单点查值. 输入格式 第一行输入一个数字 \( ...

  10. 1046 划拳 (15 分)C语言

    划拳是古老中国酒文化的一个有趣的组成部分.酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字.如果谁比划出的数字正好等于两人喊出的数字之和,谁就赢了,输家罚一杯酒.两人同赢或两人同输 ...