B.Mayor’s posters

POJ2528
题目大意:

D.Count Color

POJ2777
题目大意:长为

L

(

L

1

0

5

)

L( L\leq10^5)

L(L≤105)的序列,每个点上可以有

T

(

T

30

)

T(T\leq30)

T(T≤30)种颜色以供染色(整个序列最开始都为颜色1),

O

(

O

1

0

5

)

O(O\leq10^5)

O(O≤105)次操作:

C

C

C

a

a

a

b

b

b

c

c

c:

[

a

,

b

]

[a,b]

[a,b]染上颜色

c

c

c

P

P

P

a

a

a

b

b

b:查询

[

a

,

b

]

[a,b]

[a,b]上共有几种颜色
思路:因为

T

T

T很小,所以我们可以把颜色集合进行状态压缩,

p

u

s

h

u

p

pushup

pushup的时候直接取两个儿子的并即可,懒标记直接设为仅有对应颜色的集合即可。复杂度

O

(

(

L

+

O

)

l

o

g

L

)

O((L+O)logL)

O((L+O)logL)。此外注意本题

a

a

a可能大于

b

b

b。
代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, int> PII;
//#define int LL
#define lc p*2+1
#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 100010; struct node {
int l, r, dat, lazy;
}; node tr[maxn * 4]; void build(int p, int l, int r)
{
tr[p].l = l, tr[p].r = r, tr[p].lazy = 0;
if (l + 1 == r)
{
tr[p].dat = 1;
return;
}
int mid = (l + r) / 2;
build(lc, l, mid), build(rc, mid, r);
tr[p].dat = tr[lc].dat | tr[rc].dat;
} void pushdown(int p)
{
if (tr[p].lazy)
{
tr[lc].dat = tr[rc].dat = tr[lc].lazy = tr[rc].lazy = tr[p].lazy;
tr[p].lazy = 0;
}
} void modify(int p, int l, int r, int d)
{
if (tr[p].l >= l && tr[p].r <= r)
{
tr[p].dat = tr[p].lazy = 1 << d;
return;
}
pushdown(p);
int mid = (tr[p].l + tr[p].r) / 2;
if (l < mid)
modify(lc, l, r, d);
if (r > mid)
modify(rc, l, r, d);
tr[p].dat = tr[lc].dat | tr[rc].dat;
} int query(int p, int l, int r)
{
if(tr[p].l >= l && tr[p].r <= r)
return tr[p].dat;
pushdown(p);
int mid = (tr[p].l + tr[p].r) / 2;
if (r <= mid)
return query(lc, l, r);
if (l >= mid)
return query(rc, l, r);
return query(lc, l, mid) | query(rc, mid, r);
} int L, T, O; void solve()
{
build(0, 1, L + 1);
char t;
int a, b, c;
for (int i = 1; i <= O; i++)
{
cin >> t;
if (t == 'C')
{
cin >> a >> b >> c;
int l = min(a, b), r = max(a, b);
modify(0, l, r + 1, c - 1);
}
else
{
cin >> a >> b;
int l = min(a, b), r = max(a, b);
int num = query(0, l, r + 1), ans = 0;
for (int i = 0; i < T; i++)
ans += (num >> i) & 1;
cout << ans << endl;
}
}
} int main()
{
IOS;
cin >> L >> T >> O;
solve(); return 0;
}
E.Who Gets the Most Candies?

POJ2886
题目大意:

N

N

N个人围一圈,每个人有

A

[

i

]

A[i]

A[i],表示这个人沿顺时针

(

A

[

i

]

>

0

)

(A[i]>0)

(A[i]>0)或逆时针

(

A

[

i

]

<

0

)

(A[i]<0)

(A[i]<0)方向数的第

A

[

i

]

|A[i]|

∣A[i]∣个人是下一个要退出的,每次游戏从第

K

K

K个人开始,退出的次序

r

n

k

rnk

rnk的约束个数就是获得的糖果数,问谁获得糖果最多,有多个则输出先退出的。

思路:

i

i

i以内的约数最多的最小数字可以用倍数法

O

(

N

l

o

g

N

)

O(NlogN)

O(NlogN)预处理出来,之后对于每轮游戏直接模拟即可,用

B

I

T

BIT

BIT来维护一下各个位置上的人有没有退出,在

B

I

T

BIT

BIT上二分可以求得剩下的人中相对排名对应的原始位置。

代码:

#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<utility>
#include<cstdio>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
//#define int LL
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = (1 << 19) + 10; int cnt[maxn], S[maxn];
int N, K;
string name[maxn];
int num[maxn], dat[maxn], n; void add(int i, int x)
{
while (i <= (1 << 19))
{
dat[i] += x;
i += i & (-i);
}
} int sum(int i)
{
int ans = 0;
while (i > 0)
{
ans += dat[i];
i -= i & (-i);
} return ans;
} int getpos(int rk)
{
int k = 0, s = 0;
for (int i = 18; i >= 0; i--)
{
int t = k + (1 << i);
if (s + dat[t] < rk)
{
k = t;
s += dat[t];
}
} return k + 1;
} void init()
{
for (int i = 1; i <= 5e5; i++)
{
for (int j = i; j <= 5e5; j += i)
cnt[j]++;
}
for (int i = 1; i <= 5e5; i++)
{
if (cnt[i] > cnt[S[i - 1]])
S[i] = i;
else
S[i] = S[i - 1];
}
} void solve()
{
string ans1;
int ord = 1, lst = N - 1, ans2 = cnt[S[N]], rnk = K;
int pos = getpos(rnk), start;
while (true)
{
int A = num[pos];
ans1 = name[pos];
if (ord == S[N])
break;
add(pos, -1);
if (A > 0)
{
rnk = (rnk - 1 + A) % lst;
if (rnk == 0)
rnk = lst;
pos = getpos(rnk);
}
else
{
rnk = ((rnk + A) % lst + lst) % lst;
if (rnk == 0)
rnk = lst;
pos = getpos(rnk);
}
ord++;
lst--;
}
cout << ans1 << ' ' << ans2 << endl;
} int main()
{
IOS;
init();
while (cin >> N >> K)
{
memset(dat, 0, sizeof(dat));
for (int i = 1; i <= N; i++)
{
cin >> name[i] >> num[i];
add(i, 1);
}
solve();
} return 0;
}
F.花神游历各国

LOJ10128
题目大意:
长为

N

(

N

1

0

5

)

N(N\leq10^5)

N(N≤105)的序列,各元素

0

a

i

1

0

9

0\leq a_{i}\leq10^9

0≤ai​≤109,

M

(

M

2

×

1

0

5

)

M(M\leq2\times10^5)

M(M≤2×105)
次操作:

1

1

1

l

l

l

r

r

r:

[

l

,

r

]

[l,r]

[l,r]内所有元素开根号向下取整

2

2

2

l

l

l

r

r

r:询问

[

l

,

r

]

[l,r]

[l,r]内所有元素和

(

1

l

r

N

)

(1\leq l\leq r\leq N)

(1≤l≤r≤N)

思路:
直接向区间加上一个数那样用懒标记处理取平方根显然没有办法维护,注意到

1

0

9

10^9

109内的数最多操作6次就会变为1,而对1和0本身取根号是没有变化的,于是我们可以直接对每个区间修改暴力地进行单点修改,每个节点记录所辖区间内0与1的个数,如果全部是0或者1,那么之后就不去这个区间做修改,这样每个数最多被修改6次,每次修改

O

(

l

o

g

N

)

O(logN)

O(logN),总的复杂度还是

O

(

(

N

+

M

)

l

o

g

N

)

O((N+M)logN)

O((N+M)logN)

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, int> PII;
#define int LL
#define lc p*2+1
#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 100010; struct node {
int l, r, dat;
int cnt;//区间<=1之个数
}; int N, M;
int A[maxn];
node tr[maxn * 4]; void build(int p, int l, int r)
{
tr[p].l = l, tr[p].r = r, tr[p].cnt = 0;
if (l + 1 == r)
{
tr[p].dat = A[l];
if (A[l] <= 1)
tr[p].cnt = 1;
return;
}
int mid = (tr[p].l + tr[p].r) / 2;
build(lc, l, mid), build(rc, mid, r);
tr[p].dat = tr[lc].dat + tr[rc].dat;
tr[p].cnt = tr[lc].cnt + tr[rc].cnt;
} void modify(int p, int l, int r)
{
if (tr[p].cnt == tr[p].r - tr[p].l)//该区间不用修改
return;
if (tr[p].l + 1 == tr[p].r)
{
tr[p].dat = floor(sqrt(tr[p].dat));
if (tr[p].dat <= 1)
tr[p].cnt = 1;
return;
}
int mid = (tr[p].l + tr[p].r) / 2;
if (l < mid)
modify(lc, l, r);
if (r > mid)
modify(rc, l, r);
tr[p].dat = tr[lc].dat + tr[rc].dat;
tr[p].cnt = tr[lc].cnt + tr[rc].cnt;
} int query(int p, int l, int r)
{
if (tr[p].l >= l && tr[p].r <= r)
return tr[p].dat;
int mid = (tr[p].l + tr[p].r) / 2;
if (r <= mid)
return query(lc, l, r);
if (l >= mid)
return query(rc, l, r);
return query(lc, l, mid) + query(rc, mid, r);
} void solve()
{
build(0, 1, N + 1);
cin >> M;
int x, l, r;
for (int i = 1; i <= M; i++)
{
cin >> x;
if (x == 1)
{
cin >> l >> r;
int a = min(l, r), b = max(l, r);
cout << query(0, a, b + 1) << endl;
}
else
{
cin >> l >> r;
int a = min(l, r), b = max(l, r);
modify(0, a, b + 1);
}
}
} signed main()
{
IOS;
cin >> N;
for (int i = 1; i <= N; i++)
cin >> A[i];
solve(); return 0;
}
I.二逼平衡树

luoguP3380/LOJ106
题目大意:

思路:
树套树模板题,下标线段树中每个节点用一个动态开点权值线段树维护其区间内的元素,时空都是

O

(

N

l

o

g

2

N

)

O(Nlog^2N)

O(Nlog2N),
代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, int> PII;
//#define int LL
#define lch p*2+1
#define rch p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 50010;
const int SIZE = 17000000;//注意大小 int totb = 0;
struct nodeb {
int dat, lc, rc;
};
nodeb trb[SIZE];//内层权值线段树,动态开点 struct nodea {
int root, l, r;
};
nodea tra[maxn * 4];//外层常规下标线段树 //a:外层树操作,b:内层树操作 int buildb()
{
++totb;
trb[totb].dat = trb[totb].lc = trb[totb].rc = 0; return totb;
} void modifyb(int p, int l, int r, int val, int delta)
{
if (l + 1 == r)
{
trb[p].dat += delta;
return;
}
int mid = (l + r) / 2;
if (val < mid)
{
if (!trb[p].lc)
trb[p].lc = buildb();
modifyb(trb[p].lc, l, mid, val, delta);
}
else
{
if (!trb[p].rc)
trb[p].rc = buildb();
modifyb(trb[p].rc, mid, r, val, delta);
}
trb[p].dat = trb[trb[p].lc].dat + trb[trb[p].rc].dat;
} int rankb(int p, int l, int r, int x)
{
if (p == 0 || l + 1 == r)
return 0;
int mid = (l + r) / 2;
if (x >= mid)
return trb[trb[p].lc].dat + rankb(trb[p].rc, mid, r, x);
else
return rankb(trb[p].lc, l, mid, x);
} int N, M, A[maxn]; void builda(int p, int l, int r)
{
tra[p].root = buildb();
tra[p].l = l, tra[p].r = r;
if (l + 1 == r)
{
modifyb(tra[p].root, -1e8, 1e8 + 10, A[l], 1);
return;
}
int mid = (l + r) / 2;
builda(lch, l, mid), builda(rch, mid, r);
for (int i = l; i < r; i++)
modifyb(tra[p].root, -1e8, 1e8 + 10, A[i], 1);
} void modifya(int p, int pos, int x)
{
modifyb(tra[p].root, -1e8, 1e8 + 10, A[pos], -1);
modifyb(tra[p].root, -1e8, 1e8 + 10, x, 1);
if (tra[p].l + 1 == tra[p].r)
return;
int mid = (tra[p].l + tra[p].r) / 2;
if (pos < mid)
modifya(lch, pos, x);
else
modifya(rch, pos, x);
} int ranka(int p, int l, int r, int x)//求出[l,r)内小于x的数的个数
{
if (tra[p].l >= l && tra[p].r <= r)
return rankb(tra[p].root, -1e8, 1e8 + 10, x);
int mid = (tra[p].l + tra[p].r) / 2;
if (r <= mid)
return ranka(lch, l, r, x);
if (l >= mid)
return ranka(rch, l, r, x);
return ranka(lch, l, mid, x) + ranka(rch, mid, r, x);
} int val(int p, int l, int r, int rk)
{
int lo = -1e8, hi = 1e8 + 10;
while (hi - lo > 1)
{
int mid = (lo + hi) / 2;
if (ranka(0, l, r, mid) + 1 <= rk)//找排名<=rk的最大值
lo = mid;
else
hi = mid;
} return lo;
} int pred(int p, int l, int r, int x)
{
int rk = ranka(0, l, r, x);
if (rk < 1)
return -2147483647;
return val(0, l, r, rk);
} int succ(int p, int l, int r, int x)
{
int rk = ranka(0, l, r, x + 1) + 1;
if (rk > r - l)
return 2147483647;
return val(0, l, r, rk);
} void solve()
{
builda(0, 1, N + 1);
int opt, l, r, x, k, pos;
for (int i = 1; i <= M; i++)
{
cin >> opt;
switch (opt)
{
case 1:
cin >> l >> r >> x;
cout << ranka(0, l, r + 1, x) + 1 << endl;
break;
case 2:
cin >> l >> r >> k;
cout << val(0, l, r + 1, k) << endl;
break;
case 3:
cin >> pos >> x;
modifya(0, pos, x);
A[pos] = x;
break;
case 4:
cin >> l >> r >> x;
cout << pred(0, l, r + 1, x) << endl;
break;
case 5:
cin >> l >> r >> x;
cout << succ(0, l, r + 1, x) << endl;
break;
}
}
} int main()
{
IOS;
cin >> N >> M;
for (int i = 1; i <= N; i++)
cin >> A[i];
solve(); return 0;
}
J.冰火战士

luoguP6619/LOJ3299
思路:
当温度确定时,消耗的总能量为冰火两方可参赛战士能量总和最少的一方

×

2

\times2

×2,于是我们只要求出这个最小值即可。
发现对于温度

K

K

K,可以参赛的冰系战士总能量是一个

K

K

K处的前缀和,而可以参赛的火系战士总能量是一个

K

K

K处的后缀和,可以发现冰系战士的总能量是随着温度增加而递增的,而火系是递减的,所以显然二者最小值在二者曲线交点处取最大,但由于二者的曲线不是连续的,所以最大值有两个可能的点,分别是最大的火系总能量

\geq

≥冰系的温度

k

1

k_{1}

k1​,以及最小的火系总能量

\leq

≤冰系的温度

k

2

k_{2}

k2​,这两个点的结果取最大值就是最多能量了,我们可以对温度离散化后用

B

I

T

BIT

BIT来维护冰,火两系战士的能量,而

k

1

,

k

2

k1,k2

k1,k2两个点可以通过在

B

I

T

BIT

BIT上二分来仅用一个

l

o

g

log

log的复杂度求得,

B

I

T

BIT

BIT上的二分可以理解为就是在上面做倍增,不过要注意维护一下已经跳过部分的贡献,冰系的很好维护,火系的由于需要的是后缀和,需要额外记录一下总和

F

I

R

E

FIRE

FIRE以及每个温度处的火系选手的能量值

F

[

i

]

F[i]

F[i],温度

T

T

T时火系选手的总温度就是

F

I

R

E

S

[

T

]

+

F

[

T

]

FIRE-S[T]+F[T]

FIRE−S[T]+F[T]。题目还要求在求得最大能量的前提下,温度要尽可能地大,显然如果答案取在

k

1

k_{1}

k1​,那么就已经是最大的了,而如果取在

k

2

k_{2}

k2​,答案为

x

x

x,我们可以再在

B

I

T

BIT

BIT上面二分来寻找火系总能量

x

\geq x

≥x的温度来作为答案即可。
复杂度

O

(

Q

l

o

g

N

)

O(QlogN)

O(QlogN)

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
//#define LL int
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = (1 << 21) + 5; int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
} return x * f;
} void write(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
} int Q, M;
int N[maxn], T[maxn], X[maxn], Y[maxn], K[maxn];
int FIRE, F[(1 << 21) + 5];//fire总能量,各点上fire能量
int S[(1 << 21) + 5];
int dat[(1 << 21) + 5][2];//0:ice,1:fire int compress()
{
vector<int>xs;
for (int i = 1; i <= Q; i++)
{
if (N[i] == 1)
xs.push_back(X[i]);
}
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(), xs.end()), xs.end());
for (int i = 1; i <= Q; i++)
{
if (N[i] == 1)
{
int tmp = X[i];
X[i] = upper_bound(xs.begin(), xs.end(), X[i]) - xs.begin();
S[X[i]] = tmp;
}
} return xs.size();
} void add(int i, int x, int t)
{
while (i <= (1 << 21))
{
dat[i][t] += x;
i += i & (-i);
}
} int sum(int i, int t)
{
int ans = 0;
while (i > 0)
{
ans += dat[i][t];
i -= i & (-i);
} return ans;
} PII query1()//(最右的fire>=ice点)
{
int k = 0, fire = FIRE, ice = 0;
for (int i = 20; i >= 0; i--)
{
int t = k + (1 << i);
if (fire - dat[t][1] + F[t] >= ice + dat[t][0])
{
fire -= dat[t][1];
ice += dat[t][0];
k = t;
}
} return PII(k, sum(k, 0));
} PII query2()//(最左的fire<=ice点)
{
int k = 0, fire = FIRE, ice = 0;
for (int i = 20; i >= 0; i--)
{
int t = k + (1 << i);
if (fire - dat[t][1] + F[t] > ice + dat[t][0])
{
k = t;
fire -= dat[t][1];
ice += dat[t][0];
}
} return PII(k + 1, FIRE - sum(k + 1, 1) + F[k + 1]);
} int query3(int x)//(最右的fire>=x点)
{
int k = 0, fire = FIRE;
for (int i = 20; i >= 0; i--)
{
int t = k + (1 << i);
if (fire - dat[t][1] + F[t] >= x)
{
k = t;
fire -= dat[t][1];
}
}
return k;
} void solve()
{
M = compress();
for (int i = 1; i <= Q; i++)
{
if (N[i] == 1)
{
add(X[i], Y[i], T[i]);
if (T[i])
{
FIRE += Y[i];
F[X[i]] += Y[i];
}
}
else
{
int x = X[K[i]], y = Y[K[i]], t = T[K[i]];
if (t)
{
FIRE -= y;
F[x] -= y;
}
add(x, -y, t);
}
PII a = query1(), b = query2();
int k1 = a.first, v1 = a.second;
int k2 = b.first, v2 = b.second;
int ans1, ans2;
if (v1 > v2)
ans2 = v1, ans1 = k1;
else
ans2 = v2, ans1 = k2;
if (ans2 == v2)
ans1 = query3(ans2);
if (!ans2)
puts("Peace");
else
{
write(S[ans1]), putchar(' ');
write(ans2 * 2), putchar('\n');
}
}
} int main()
{
Q = read();
for (int i = 1; i <= Q; i++)
{
N[i] = read();
if (N[i] == 1)
T[i] = read(), X[i] = read(), Y[i] = read();
else
K[i] = read();
}
solve(); return 0;
}
K.混合果汁

luoguP4602/LOJ2555
题目大意:

N

(

N

1

0

5

)

N(N\leq10^5)

N(N≤105)种果汁,每种果汁有美味度

d

i

d_{i}

di​,单价

p

i

p_{i}

pi​,购买上限

l

i

(

1

d

i

,

p

i

,

l

i

1

0

5

)

l_{i}(1\leq d_{i},p_{i},l_{i}\leq10^5)

li​(1≤di​,pi​,li​≤105),有

M

(

M

1

0

5

)

M(M\leq10^5)

M(M≤105)次询问,每次询问给出拥有的钱

g

i

g_{i}

gi​,至少购买的果汁升数

L

i

(

1

g

i

,

L

i

1

0

18

)

L_{i}(1\leq g_{i},L_{i}\leq10^{18})

Li​(1≤gi​,Li​≤1018),混合果汁的美味度为所购买的果汁中美味度最低的,对于每个询问,需要使所购买满足要求的混合果汁中美味度最大,如果不能购买满足要求的果汁,输出

1

-1

−1。

思路:
我们考虑单独

1

1

1个询问,显然我们可以二分美味度

d

d

d,

c

h

e

c

k

check

check时考虑所有

d

i

d

d_{i}\geq d

di​≥d的果汁,贪心地从单价低的开始购买,看能不能卖出满足要求的即可。
现在有多组询问,每次都直接二分显然是不行的,于是我们可以建一个主席树维护单价

p

p

p上的信息,对所有的

d

d

d从大到小可持久化(实现时对果汁按

d

d

d排序之后对下标可持久化,二分也改为二分下标即可),每个节点维护所辖区间的果汁总购买上限以及购买他们所需要的总价格,对于每个查询,二分

c

h

e

c

k

check

check时只需要查询主席树上对应的版本就可以了,这样每个询问可以在

O

(

l

o

g

2

N

)

O(log^2N)

O(log2N)的时间内回答,总的复杂度为

O

(

N

l

o

g

2

N

)

O(Nlog^2N)

O(Nlog2N)。

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
#define int LL
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 100010; struct node {
int lc, rc, sum, lim;
}tr[maxn * 40];
struct juice {
int d, p, l;
}J[maxn];
int tot = 0, root[maxn], N, M, mx = -INF; bool cmp(const juice& a, const juice& b)
{
return a.d < b.d;
} int build(int l, int r)//[)
{
int p = ++tot;
if (l + 1 == r)
{
tr[p].lim = tr[p].sum = 0;
return p;
}
int mid = (l + r) / 2;
tr[p].lc = build(l, mid), tr[p].rc = build(mid, r);
tr[p].lim = tr[tr[p].lc].lim + tr[tr[p].rc].lim;
tr[p].sum = tr[tr[p].lc].sum + tr[tr[p].rc].sum; return p;
} int modify(int now, int l, int r, int x, int val)
{
int p = ++tot;
tr[p] = tr[now];
if (l + 1 == r)
{
tr[p].sum += x * val, tr[p].lim += val;
return p;
}
int mid = (l + r) / 2;
if (x < mid)
tr[p].lc = modify(tr[now].lc, l, mid, x, val);
else
tr[p].rc = modify(tr[now].rc, mid, r, x, val);
tr[p].lim = tr[tr[p].lc].lim + tr[tr[p].rc].lim;
tr[p].sum = tr[tr[p].lc].sum + tr[tr[p].rc].sum; return p;
} bool query(int p, int q, int l, int r, int k, int s)//k:购买上限,s:所持有的钱数
{
if (k > tr[q].lim - tr[p].lim || s < 0)
return false;
if (l + 1 == r)
return l * k <= s;
int mid = (l + r) / 2;
int llim = tr[tr[q].lc].lim - tr[tr[p].lc].lim, lsum = tr[tr[q].lc].sum - tr[tr[p].lc].sum;
if (k <= llim)
return query(tr[p].lc, tr[q].lc, l, mid, k, s);
else
return query(tr[p].rc, tr[q].rc, mid, r, k - llim, s - lsum);
} void solve()
{
sort(J + 1, J + N + 1, cmp);
int lst = J[N].d;
root[0] = build(1, mx + 1);
for (int i = N; i >= 1; i--)
root[N - i + 1] = modify(root[N - i], 1, mx + 1, J[i].p, J[i].l);
int g, l;
for (int i = 1; i <= M; i++)
{
cin >> g >> l;
int lo = 0, hi = N + 1;//对下标二分
while (hi - lo > 1)
{
int mid = (hi + lo) / 2;
if (query(root[0], root[N - mid + 1], 1, mx + 1, l, g))
lo = mid;
else
hi = mid;
}
cout << (lo ? J[lo].d : -1) << endl;
}
} signed main()
{
IOS;
cin >> N >> M;
for (int i = 1; i <= N; i++)
{
cin >> J[i].d >> J[i].p >> J[i].l;
mx = max(mx, J[i].p);
}
solve(); return 0;
}
L.陌上花开

luoguP3810/LOJ112
题目大意:

1

N

1

0

5

,

1

K

2

×

1

0

5

1\leq N\leq10^5,1\leq K\leq2\times10^5

1≤N≤105,1≤K≤2×105

思路:
三维偏序模板题,第一维排序,第二维用树状数组维护,第三维用动态开点的权值线段树维护。
(树状数组的每个节点维护一颗维护其所辖第二维区间上的第三维值域上面信息的线段树。)

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, int> PII;
//#define int LL
#define lch p*2+1
#define rch p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 200010;
const int SIZE = 30000000; int tot = 0; struct point {
int a, b, c, id;
bool operator<(const point& rhs)
{
if (a == rhs.a)
{
if (b == rhs.b)
return c < rhs.c;
return b < rhs.b;
}
return a < rhs.a;
}
bool operator==(const point& rhs)
{
return a == rhs.a && b == rhs.b && c == rhs.c;
}
}; struct node {
int dat, lc, rc;
};
node tr[SIZE];//内层权值线段树,动态开点 int build()
{
++tot;
tr[tot].dat = tr[tot].lc = tr[tot].rc = 0; return tot;
} void modify(int p, int l, int r, int val, int delta)
{
if (l + 1 == r)
{
tr[p].dat += delta;
return;
}
int mid = (l + r) / 2;
if (val < mid)
{
if (!tr[p].lc)
tr[p].lc = build();
modify(tr[p].lc, l, mid, val, delta);
}
else
{
if (!tr[p].rc)
tr[p].rc = build();
modify(tr[p].rc, mid, r, val, delta);
}
tr[p].dat = tr[tr[p].lc].dat + tr[tr[p].rc].dat;
} int query(int p, int l, int r, int x)
{
if (p == 0 || l + 1 == r)
return 0;
int mid = (l + r) / 2;
if (x >= mid)
return tr[tr[p].lc].dat + query(tr[p].rc, mid, r, x);
else
return query(tr[p].lc, l, mid, x);
} int N, K;
int ans[maxn];
point A[maxn];
int dat[maxn], n; void add(int posx, int posy, int x)
{
while (posx <= n)
{
if (!dat[posx])
dat[posx] = build();
modify(dat[posx], 1, K + 5, posy, x);
posx += posx & (-posx);
}
} int sum(int posx, int posy)
{
int tmp = 0;
while (posx > 0)
{
tmp += query(dat[posx], 1, K + 5, posy + 1);
posx -= posx & (-posx);
} return tmp;
} void solve()
{
n = K + 5;
sort(A + 1, A + N + 1);
int tmp = 1;
for (int i = 1; i <= N; i++)
{
if (A[i] == A[i + 1])
{
tmp++;
continue;
}
add(A[i].b, A[i].c, tmp);
ans[sum(A[i].b, A[i].c) - 1] += tmp;
tmp = 1;
}
for (int i = 0; i < N; i++)
cout << ans[i] << endl;
} int main()
{
IOS;
cin >> N >> K;
for (int i = 1; i <= N; i++)
{
cin >> A[i].a >> A[i].b >> A[i].c;
A[i].id = i;
}
solve(); return 0;
}
M.A Plus B Problem

2021CCPC桂林站B
题目大意:
两个长度为

N

(

N

1

0

6

)

N(N\leq10^6)

N(N≤106)的数字

A

,

B

A,B

A,B做加法,结果也为一个长度为

N

N

N的数字(多出来的高位被忽略),

Q

(

Q

1

0

6

)

Q(Q\leq10^6)

Q(Q≤106)个次操作,每次操作把选择两个加数的其中一个,将其某一位数字替换为

0

0

0到

9

9

9中的一个数字,询问每次操作后,两个加数以及和中,总共有多少数字发生变化。

思路:
主要是要解决更改数字后的进位问题,对于每一位,最多向前进

1

1

1位,所以只要该位上两个加数的数字相加不等于

9

9

9,那么这一位向前进位与否就仅取决于自己,否则还要取决于更低一位是否进位,设当前位为

i

i

i,

i

i

i右边的第一个加数和不为

9

9

9的位记为

j

j

j,那么第

i

i

i位和数上的值就等于

(

A

i

+

B

i

+

[

A

j

+

B

j

10

]

)

%

10

(A_{i}+B_{i}+[A_{j}+B_{j}\geq 10])\%10

(Ai​+Bi​+[Aj​+Bj​≥10])%10
那么显然如果更改后某一位上的进位状态没有发生变化,就仅会改变

2

2

2个数字,如果发生了变化,那么受其原先进位状态影响的若干个连续的更高位也会发生变化,记

i

i

i位左侧首个加数和不为

9

9

9的位为

j

j

j,

i

i

i位所能影响的就是

j

j

j到

i

i

i这若干位,于是变化的位数就是这些位数

+

1

+1

+1了。
我们可以维护所有加数和不等于

9

9

9的位,并且要插入,删除,查询前驱后继,所以我们用

s

e

t

set

set就可以轻松维护,在

O

(

Q

l

o

g

N

)

O(QlogN)

O(QlogN)内解决问题。
注意一些边界情况要特别处理一下。

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
//#define int LL
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 1000010; int N, Q;
string S[2];
int num[2][maxn];
set<int>ss; void solve()
{
ss.insert(N + 1), ss.insert(0);
for (int i = 1; i <= N; i++)
{
num[0][i] = S[0][i - 1] - '0', num[1][i] = S[1][i - 1] - '0';
if (num[0][i] + num[1][i] != 9)
ss.insert(i);
}
int r, c, d;
for (int i = 1; i <= Q; i++)
{
int ans1 = 2, ans2;
cin >> r >> c >> d;
bool up1 = false, up2 = false;
int up = 0;
int a = num[1][c] + num[0][c];
int succ = *ss.upper_bound(c);
up = num[1][succ] + num[0][succ] >= 10;
if (a + up >= 10)
up1 = true;
if (a != 9)
ss.erase(c);
num[r - 1][c] = d;
int b = num[1][c] + num[0][c];
if (b != 9)
ss.insert(c);
succ = *ss.upper_bound(c);
up = num[1][succ] + num[0][succ] >= 10;
if (b + up >= 10)
up2 = true;
ans2 = (b + up) % 10;
if (a == b)
{
cout << ans2 << ' ' << 0 << endl;
continue;
}
if (up1 ^ up2)
{
int pred = *(--ss.lower_bound(c));
ans1 += c - pred;
if (pred == 0)
ans1--;
}
cout << ans2 << ' ' << ans1 << endl;
}
} int main()
{
IOS;
cin >> N >> Q >> S[0] >> S[1];
solve(); return 0;
}

AHUACM寒假集训II(线段树)的更多相关文章

  1. UVA10869 - Brownie Points II(线段树)

    UVA10869 - Brownie Points II(线段树) 题目链接 题目大意:平面上有n个点,Stan和Ollie在玩游戏,游戏规则是:Stan先画一条竖直的线作为y轴,条件是必需要经过这个 ...

  2. 2020牛客寒假算法基础集训营3 - G. 牛牛的Link Power II(线段树)

    题目链接:牛牛的Link Power II 题意:给你一个只含$0$和$1$的串,定义串的$Link$值为串中两个的$1$之间的距离的和,$(u,v)$和$(v,u)$被看认为是同一对,有$m$次操作 ...

  3. CDOJ 1259 昊昊爱运动 II 线段树+bitset

    昊昊爱运动 II 昊昊喜欢运动 他N天内会参加M种运动(每种运动用一个[1,m]的整数表示) 现在有Q个操作,操作描述如下 昊昊把第l天到第r天的运动全部换成了x(x∈[1,m]) 问昊昊第l天到第r ...

  4. SPOJ 1557. Can you answer these queries II 线段树

    Can you answer these queries II Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 https://www.spoj.com/pr ...

  5. bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

    2482: [Spoj1557] Can you answer these queries II Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 145 ...

  6. 4990: [Usaco2017 Feb]Why Did the Cow Cross the Road II 线段树维护dp

    题目 4990: [Usaco2017 Feb]Why Did the Cow Cross the Road II 链接 http://www.lydsy.com/JudgeOnline/proble ...

  7. hdu 5831 Rikka with Parenthesis II 线段树

    Rikka with Parenthesis II 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5831 Description As we kno ...

  8. 【CF687D】Dividing Kingdom II 线段树+并查集

    [CF687D]Dividing Kingdom II 题意:给你一张n个点m条边的无向图,边有边权$w_i$.有q个询问,每次给出l r,问你:如果只保留编号在[l,r]中的边,你需要将所有点分成两 ...

  9. 【BZOJ2482】[Spoj1557] Can you answer these queries II 线段树

    [BZOJ2482][Spoj1557] Can you answer these queries II Description 给定n个元素的序列. 给出m个询问:求l[i]~r[i]的最大子段和( ...

随机推荐

  1. gin中获取查询字符串参数

    package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := ...

  2. Linux深入探索04-Bash shell

    ----- 最近更新[2021-12-30]----- 本文目录结构预览: 一.简介 二.shell 变量 1.查看变量 2.变量类型 3.变量操作 4.系统常见的全局变量 三.shell 选项 1. ...

  3. python浮点数计算--5

    #!/usr/bin/python #coding=utf-8 i=1.0 j=3 print(i*j) print(i+j) print(i**j) 备注:无论是哪种运算,只要有操作数是浮点数,py ...

  4. VsCode配置C/C++开发环境

    Visual Studio Code(VS Code)是基于 Electron 开发,支持 Windows.Linux 和 macOS 操作系统.内置了对JavaScript,TypeScript和N ...

  5. python编写购物车新写法

    用另一种方式完成购物车的功能实现 #!/usr/bin/python zijin = input("请输入资金:") if zijin.isdigit(): zijin = int ...

  6. python开发: linux进程打开的文件数

    1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 4 ''' 统计linux打开的文件数 ''' 5 6 import os 7 import sys ...

  7. js添加元素代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. NSMutableDictionary基本概念

    1.NSMutableDictionary 基本概念 什么是NSMutableDictionary NSMutableDictionary是NSDictionary的子类 NSDictionary是不 ...

  9. Python起航

    安装Python 安装python 添加python安装目录到PATH 添加Scripts目录到PATH 如果同时安装了python2和python3,那么通过python和python3,pip和p ...

  10. TableView 常用技巧与功能详解

    分割线顶格iOS8 UITableview分割线顶格的做法 //iOS8 Cell分割线顶格 if ([_tableView respondsToSelector:@selector(setSepar ...