タイトル
分析
問題の意味:列数\(A \)プレフィックスの最大数を求めて、動的な変更、。場合(I \)\満足\(\ MAX_ 1} = {J ^ {I} 1-a_j <a_iを\) 、次いで\(a_iを\)は、プレフィックスの最大値です。(本当に最長のサブシーケンスの増大した長さを求めていません!)
間隔維持セグメントツリー、考える\([L、R]を\ ) 維持する\([L、R] \ ) の最大値とのサブ間隔\([L、R]を\ ) (プレフィクスの最大数に音符だけ間隔を検討すること\([L、R&LT] \)を、考慮していない([1、L)\ \ ) の影響。これを以下「答え」)と呼ばれています。合併またはその範囲は、最大値を直接マージすることができ、そしてその答えはあまりにも面倒合併する場合:
答えは、直接2のサブ間隔が追加された答えではありません。答えは左のセクションでは、回答の現在の範囲の中に直接添加することができます。左部分の最大値(X \)\、のすべての権利間隔未満\(X \)プレフィックスの最大値には、アウトカウントすることができません。したがって、現在の間隔の権利範囲の寄与が正解間隔である「すべてがより大きい(X \)\プレフィックスの最大数」。
「より大きい最大プレフィックスが単調に増加しなければならないので、それほど(X \)\プレフィックス最大」サフィックスは右接頭語の最大間隔はバイナリサーチと同様の方法によって解決することが可能でなければなりません。左が最大値よりも大きい場合具体的には、離れて現在のレンジから右下、\(X- \) 、の左に説明し、現在計算された応答(現在のインターバル来ない!)貢献し、検索するために、左にセクション貢献プラス電流範囲に移動する権利しばらく(ない正解!);検索するそれ以外の場合は、権利。
このプロセスを実現するために、各非リーフ部分も解答セクションの右側部分の寄与を記録し、理論的には、上記の方法に従って完成ダウン考慮しなければならないが、理由は左セクションの左部分の寄与の答えですので、直接答えは答えの電流範囲とその貢献マイナス間隔左右のセクションです。時間複雑\(O(N \ログ^ 2N)\) 。
コード
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
const int N = 1e5 + 10;
namespace Segment_Tree
{
struct node
{
double mx;
int ans;
}tree[N << 2];
int cal(const int rot, const int lt, const int rt, const double lim)
{
if (lt == rt)
return tree[rot].mx > lim ? 1 : 0;
int mid = (lt + rt) >> 1;
if (tree[rot << 1].mx < lim)
return cal(rot << 1 | 1, mid + 1, rt, lim);
else
return tree[rot].ans - tree[rot << 1].ans + cal(rot << 1, lt, mid, lim);
}
void update(const int rot, const int lt, const int rt)
{
tree[rot].mx = max(tree[rot << 1].mx, tree[rot << 1 | 1].mx);
int mid = (lt + rt) >> 1;
tree[rot].ans = tree[rot << 1].ans + cal(rot << 1 | 1, mid + 1, rt, tree[rot << 1].mx);
}
void change(const int rot, const int lt, const int rt, const int pos, const double x)
{
if (lt == rt)
return void((tree[rot].mx = x, tree[rot].ans = (x > 0 ? 1 : 0)));
int mid = (lt + rt) >> 1;
if (pos <= mid)
change(rot << 1, lt, mid, pos, x);
else
change(rot << 1 | 1, mid + 1, rt, pos, x);
update(rot, lt, rt);
}
int query()
{
return tree[1].ans;
}
}
int n, m;
int work()
{
using Segment_Tree::change;
using Segment_Tree::query;
read(n), read(m);
for (int i = 1; i <= n; i++)
change(1, 1, n, i, 0);
while (m--)
{
int x, y;
read(x), read(y);
change(1, 1, n, x, (double)y / x);
write(query()), putchar('\n');
}
return 0;
}
}
int main()
{
return zyt::work();
}