字符处理
[题目描述]
妈妈的工作是英语翻译,经常和英语字符串打交道,今天妈妈给了 Tom 一个只有小写字 母构成的字符串,需要 Tom 做以下工作:
要是连续出现相同的小写字母,则把他们替换成这个字母的大写形式,后面紧跟相同字 母的个数,并把它之前跟之后的两段字串调
换,例如出现 bcaaaaaaef,则新字符串变成: efA6bc,然后重新扫描字串,直到没有出现连续相同的小写字母为止。
Tom 觉得自己字符串部分没有学好,请你帮帮他。
[输入格式]
输入一行小写字母构成的原始字符串(字符串长度不大于 250)。
[输出描述] 按妈妈要求输出新的字符串。
[输入样例 1] bcaaaaaaef [输出样例 1] efA6bc
[输入样例 2] cmmmcefffg [输出样例 2] gM3cF3c
题解:模拟,然后因为自己不仔细挂掉了50分,因为当长度大于10的时候,不能直接转
以后还是要花时间检查一下前面的题,挂分太可怕了
#include<bits/stdc++.h>
#define N 300
using namespace std;
char a[N], b[N];
int lena, lenb, sta[N];
void calc(int pos, int len){
if(len < 2) return;
int l = pos - len, r = pos - 1;
lenb = 0;
for(int i=r+1; i<=lena; i++){
b[++lenb] = a[i];
}
b[++lenb] = (a[r] - 'a' + 'A');
int top = 0;
while(len) sta[++top] = len % 10, len /= 10;
while(top) b[++lenb] = (sta[top] + '0'), top--;
for(int i=1; i<=l-1; i++){
b[++lenb] = a[i];
}
for(int i=1; i<=lenb; i++) a[i] = b[i];
lena = lenb;
}
int main(){
scanf("%s", a+1); lena = strlen(a+1);
a[0] = '&';
int len = 0;
die:;
for(int i=1; i<=lena; i++){
if(a[i] == a[i-1]) len++;
else{
if(len == 0) continue;
calc(i, len + 1);
len = 0; goto die;
}
}
for(int i=1; i<=lena; i++) printf("%c", a[i]);
return 0;
}
棋盘问题
[题目描述]小 O 对国际象棋有着浓厚的兴趣,因为他水平高超,每次人机对战他总是轻松获胜,所以他决定自己跟自己下国
际象棋。 小 O 的棋盘非常大,达到了 10^9,现在他在棋盘上摆放了 n 个国王,并对你提出 了 q 次询问,每次询问指定一个坐
标,问将所有国王从初始位置全部移动到这个坐标所需要 的最小步数是多少,询问之间相互独立,也就是说每次询问结束后国王
会全部回到原来位置。
注意:由于小 O 担心大家无法理解过于高深的规则,所以在本题中,国王之间不会发生 相互攻击而且多个国王可以同时处在一
个格子中, 国际象棋中国王一步只能移动到与其八 连通的格子中。
[输入格式] 第一行一个正整数 T 表示数据组数。 对于每组数据,共有(n+q+1)行: 第一行两个数字 n,q 分别表示国王数量和
询问数量。 接下来 n 行,每行两个数字 Kxi,Kyi 表示国王所在坐标。 接下来 q 行,每行两个数字 Txi,Tyi 表示目标坐标。
[输出格式] 对于每组数据,输出共有 q 行,每行一个整数表示对应询问的答案。
[输入样例]
1
1 1
233 666
666 233
[输出样例]
433
[数据范围]
本题共 7 个测试点,不采用 subtask 评测,但每个测试点分值不同.
数据范围中的 X,Y 范围表示 Kxi,Kyi,Txi,Tyi 的范围,未标注即为没有特殊限制 对于全部数据
满足 N 的总和不超过 10^6 且 Q 的总和不超过 10^6
题解:切比雪夫距离转曼哈顿距离,然后求前缀和就可以了
然后询问的时候二分一下坐标,以后还是自己写二分,不要用lower_bound了
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
typedef long long ll;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
return cnt * f;
}
int T, n, m;
int x[N], y[N]; ll sumx[N], sumy[N];
void print(ll x){
if(x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int main(){
T = read();
while(T--){
n = read(), m = read();
for(int i=1; i<=n; ++i){
int p = read(), q = read();
x[i] = p + q; y[i] = p - q;
}
sort(x+1, x+n+1); sort(y+1, y+n+1);
for(int i=1; i<=n; ++i){
sumx[i] = sumx[i-1] + x[i];
sumy[i] = sumy[i-1] + y[i];
}
while(m--){
int p = read(), q = read();
int xx = p + q, yy = p - q;
ll ans = 0;
int l = 0, r = n;
while(l < r){
int mid = (l+r+1) >> 1;
if(x[mid] <= xx) l = mid;
else r = mid - 1;
}
ans += 1ll * l * xx - sumx[l];
ans += (sumx[n] - sumx[l]) - 1ll * (n - l) * xx;
l = 0, r = n;
while(l < r){
int mid = (l+r+1) >> 1;
if(y[mid] <= yy) l = mid;
else r = mid - 1;
}
ans += 1ll * l * yy - sumy[l];
ans += (sumy[n] - sumy[l]) - 1ll * (n - l) * yy;
print(ans >> 1); puts("");
}
} return 0;
}
简单数据结构
[题目描述]
在看了 jiry_2 的课件《Segment Tree Beats!》后,小 O 深深沉迷于这种能单次 O(logn) 支持区间与一个数取 min/max,
查询区间和等信息的数据结构,于是他决定做一道与区间与 一个数取 min/max 的好题。
这题是这样的:你得到了一个长度为 n 的数列{ai}, 要求支持以下 2 种操作:
第一种是给 定 L,R,X,要求把区间中比 X 小的数字全部修改为 X;
第二种是给定 L,R,K,X,查询区间中比 X 小的最小的 K 个数,并且将它们升序输出,没有则输出-1。
小 O 觉得这题太简单了,于是把这题丢给了你,请你帮忙实现。
[输入格式] 第一行一个数字 n 表示数列长度, 第二行 n 个数字分别表示 a1....an, 第三行一个数字 m 表示操作次数,
接下来 m 行每行表示一次操作, 第一个数 op 表示操作类型,op 可能是 1 或 2,
如果 op=1,后面有 L,R,X 三个正整数表示把区间[L,R]中比 X 小的数字全部改成 X
如果 op=2,后面有 L,R,X,K 四个正整数表示查询区间[L,R]中比 X 小的最小的 K 个数
[输出格式] 对于每个 op=2,输出一行, 如果比 X 小的数达到了 K 个,升序输出最小的 K 个数, 如果比 X 小的数小于 K 个,
输出一行一个-1 即可.
[数据范围] 本题共 6 个测试点,不采用 subtask 评测,但每个测试点分值不同。 对于全部数据,满足 1<=n,m<=500000,1<=L<=R<=n,1<=K<=n,1<=Ai,X<=10^9,
对于所有 操作 2 中的 K,K 的总和不超过 5*10^6。
#1:12pts,满足 1<=n,m<=3000;
#2:7pts,满足 1<=n,m<=100000, 没有操作 1,且对于所有操作 2 有 K=1;
#3:23pts,满足 1<=n,m<=100000,对于所有操作 2 有 K=1;
#4:37pts,满足 1<=n,m<=100000,没有操作 1;
#5:6pts,满足 1<=n,m<=100000;
#6:15pts,无特殊限制。
关于 Subtask 的写法
Case 1 : O(n^2 log n)
Case 2 : 线段树 / st 表 区间最小
Case 3 : 线段树随便维护一下最小(我被PPT误导了,还维护了次小)
Case 4: 我们发现我们需要输出区间前 k 小,加上没有修改,所以可以主席树
Case 5: PPT提示我们分块,所以应该可以 O(n sqrt(n) log(n))
大概就是每个块暴力排序然后每次修改暴力二分位置
写出前4个点就可以得到 79 分的好成绩
#include<bits/stdc++.h>
#define N 200050
using namespace std;
typedef long long ll;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, a[N], m, b[N], siz;
struct Quary{int l, r, x, k;}q[N];
int flag1; // without operation 1 -> 44pts
int flag2; // with operation 2 for k = 1 -> 23pts
int k;
struct President{
int tot, rt[N], ls[N * 20], rs[N * 20], sum[N * 20];
void Build(int &x, int l, int r){
x = ++tot; if(l == r) return;
int mid = (l+r) >> 1;
Build(ls[x], l, mid); Build(rs[x], mid+1, r);
}
void Insert(int &x, int last, int l, int r, int pos){
x = ++tot; ls[x] = ls[last]; rs[x] = rs[last]; sum[x] = sum[last] + 1;
if(l == r) return; int mid = (l+r) >> 1;
if(pos <= mid) Insert(ls[x], ls[last], l, mid, pos);
else Insert(rs[x], rs[last], mid+1, r, pos);
}
int Query(int a, int b, int l, int r, int L, int R){
if(!a) return 0; if(L<=l && r<=R) return sum[a] - sum[b];
int mid = (l+r) >> 1, ans = 0;
if(L<=mid) ans += Query(ls[a], ls[b], l, mid, L, R);
if(R>mid) ans += Query(rs[a], rs[b], mid+1, r, L, R);
return ans;
}
void Dfs(int x, int y, int l, int r){
if(!k) return;
if(l == r){
for(int i=1; i <= min(k, sum[x] - sum[y]); i++) printf("%d ", b[l]);
k -= (sum[x] - sum[y]);
return;
}
int mid = (l+r) >> 1;
if(sum[ls[x]] - sum[ls[y]]) Dfs(ls[x], ls[y], l, mid);
if(sum[rs[x]] - sum[rs[y]]) Dfs(rs[x], rs[y], mid+1, r);
}
void Cout(int a, int b, int l, int r, int k){
if(!a) return; if(!k) return;
if(sum[a] - sum[b] >= k){ Dfs(a, b, l, r); return;}
int mid = (l+r) >> 1, val = sum[ls[a]] - sum[ls[b]];
Cout(ls[a], ls[b], l, mid, min(k, val));
if(k > val) Cout(rs[a], rs[b], mid+1, r, k - val);
}
}P;
void Solve0(){ // n ^ 2 * log n 保佑能过
for(int C = 1; C <= m; C++){
int l = q[C].l, r = q[C].r, x = q[C].x, k = q[C].k;
if(!k){ for(int i=l; i<=r; i++){ if(a[i] < x) a[i] = x;}}
else{
int len = 0;
for(int i=l; i<=r; i++) b[++len] = a[i];
sort(b+1, b+len+1); int cnt = 0;
for(int i=1; i<=len; i++){ if(b[i] >= x) break; cnt++;}
if(cnt < k) printf("-1\n");
else{ for(int i=1; i<=k; i++) printf("%d ", b[i]); printf("\n");}
}
}
}
void Solve1(){ // president tree // no modify
for(int i=1; i<=n; i++) b[++siz] = a[i];
for(int i=1; i<=m; i++) b[++siz] = q[i].x - 1;
sort(b+1, b+siz+1);
siz = unique(b+1, b+siz+1) - (b+1);
P.Build(P.rt[0], 1, siz);
for(int i=1; i<=n; i++){
int x = lower_bound(b+1, b+siz+1, a[i]) - b;
P.Insert(P.rt[i], P.rt[i-1], 1, siz, x);
}
for(int i=1; i<=m; i++){
int l = q[i].l, r = q[i].r, x = q[i].x - 1; k = q[i].k;
x = lower_bound(b+1, b+siz+1, x) - b;
int sum = P.Query(P.rt[r], P.rt[l-1], 1, siz, 1, x);
if(sum < k) printf("-1\n");
else P.Cout(P.rt[r], P.rt[l-1], 1, siz, k), printf("\n");
}
}
const int inf = 0x3fffffff;
struct Segment{
int mi[N << 1], se[N << 1], tag[N << 1];
void Pushup(int x){
if(mi[x<<1] < mi[x<<1|1]){
mi[x] = mi[x<<1];
if(se[x<<1] < mi[x<<1|1]) se[x] = se[x<<1];
else se[x] = mi[x<<1|1];
}
else if(mi[x<<1] > mi[x<<1|1]){
mi[x] = mi[x<<1|1];
if(se[x<<1|1] < mi[x<<1]) se[x] = se[x<<1|1];
else se[x] = mi[x<<1];
}
else{
mi[x] = mi[x<<1];
if(se[x<<1] < se[x<<1|1]) se[x] = se[x<<1];
else se[x] = se[x<<1|1];
}
}
void Build(int x, int l, int r){
if(l == r){ mi[x] = a[l]; se[x] = inf; return;}
int mid = (l+r) >> 1;
Build(x<<1, l, mid); Build(x<<1|1, mid+1, r);
Pushup(x);
}
void Pushdown(int x){
if(tag[x]){
if(mi[x<<1] < tag[x]) mi[x<<1] = tag[x], tag[x<<1] = tag[x];
if(mi[x<<1|1] < tag[x]) mi[x<<1|1] = tag[x], tag[x<<1|1] = tag[x];
tag[x] = 0;
}
}
void Dfs(int x, int l, int r, int v){
if(mi[x] >= v) return;
if(mi[x] < v && se[x] >= v){ mi[x] = v; tag[x] = v; return;}
if(l == r){ mi[x] = v; se[x] = inf; return;} Pushdown(x);
int mid = (l+r) >> 1;
Dfs(x<<1, l, mid, v);
Dfs(x<<1|1, mid+1, r, v);
Pushup(x);
}
void Modify(int x, int l, int r, int L, int R, int v){
if(L<=l && r<=R){ Dfs(x, l, r, v); return;}
Pushdown(x);
int mid = (l+r) >> 1;
if(L<=mid) Modify(x<<1, l, mid, L, R, v);
if(R>mid) Modify(x<<1|1, mid+1, r, L, R, v);
Pushup(x);
}
int Query(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return mi[x];
Pushdown(x);
int mid = (l+r) >> 1, ans = 2e9;
if(L<=mid) ans = min(ans, Query(x<<1, l, mid, L, R));
if(R>mid) ans = min(ans, Query(x<<1|1, mid+1, r, L, R));
return ans;
}
}S;
void Solve2(){
S.Build(1, 1, n);
for(int C = 1; C <= m; C++){
int l = q[C].l, r = q[C].r, x = q[C].x, k = q[C].k;
if(!k){ S.Modify(1, 1, n, l, r, x);}
else{
int v = S.Query(1, 1, n, l, r);
if(v >= x) printf("-1\n");
else printf("%d \n", v);
}
}
}
int main(){
freopen("segtree.in","r",stdin);
freopen("segtree.out","w",stdout);
n = read();
for(int i=1; i<=n; i++) a[i] = read();
m = read();
for(int i=1; i<=m; i++){
int op = read(), l = read(), r = read(), x = read();
if(op == 1) q[i] = (Quary){l, r, x, 0}, flag1 = 1;
else{
int k = read(); q[i] = (Quary){l, r, x, k};
if(k != 1) flag2 = 1;
}
}
if(n <= 3000 && m <= 3000){ Solve0(); return 0;}
if(!flag1){ Solve1(); return 0;}
if(!flag2){ Solve2(); return 0;}
return 0;
}
正解
有一个求区间前 k 大的套路, 就是用堆维护
我们将一个区间 , 放进优先队列,pos 表示最小值的位置,val 表示最小值
取出后,将 l -- pos-1, pos+1 -- r 插入堆,重复 k 次
我们发现对于区间取Max,最小值的点不会变,变的只会是值
于是我们可以线段树维护区间最小值,以及区间最小值的位置,修改标记,就可以完美的A掉这道题
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
typedef long long ll;
inline int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
inline void print(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int n, a[N], m;
const int inf = 0x3fffffff;
#define pa pair<int, int>
#define mp make_pair
struct Segment{
int mi[N << 1], tag[N << 1], pos[N << 1];
inline void Pushup(int x){
if(mi[x<<1] < mi[x<<1|1]) mi[x] = mi[x<<1], pos[x] = pos[x<<1];
else mi[x] = mi[x<<1|1], pos[x] = pos[x<<1|1];
}
inline void Build(int x, int l, int r){
if(l == r){ mi[x] = a[l]; pos[x] = l; return;}
int mid = (l+r) >> 1;
Build(x<<1, l, mid); Build(x<<1|1, mid+1, r);
Pushup(x);
}
inline void Pushnow(int x, int v){ tag[x] = max(tag[x], v); mi[x] = max(mi[x], v);}
inline void Pushdown(int x){ if(tag[x]){ Pushnow(x<<1, tag[x]); Pushnow(x<<1|1, tag[x]); tag[x] = 0;}}
void Modify(int x, int l, int r, int L, int R, int v){
if(L<=l && r<=R){ Pushnow(x, v); return;} Pushdown(x); int mid = (l+r) >> 1;
if(L<=mid) Modify(x<<1, l, mid, L, R, v);
if(R>mid) Modify(x<<1|1, mid+1, r, L, R, v); Pushup(x);
}
inline pa Merge(pa x, pa y){ if(x.first < y.first) return x; else return y;}
pa Query(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return mp(mi[x], pos[x]); Pushdown(x);
int mid = (l+r) >> 1; pa ans = mp(inf, 0);
if(L<=mid) ans = Merge(ans, Query(x<<1, l, mid, L, R));
if(R>mid) ans = Merge(ans, Query(x<<1|1, mid+1, r, L, R));
return ans;
}
}S;
struct Node{
int l, r, pos, val;
Node(int _l = 0, int _r = 0){
l = _l, r = _r; pa tmp = S.Query(1, 1, n, l, r);
val = tmp.first, pos = tmp.second;
}
inline bool operator < (const Node &a) const{ return val > a.val;}
};
int sta[N];
inline void Query(int l, int r, int x, int k){
priority_queue<Node> q;
q.push(Node(l, r));
for(register int i=1; i<=k; ++i){
Node now = q.top(); q.pop();
if(now.val >= x){ puts("-1"); return;}
sta[i] = now.val;
if(now.l <= now.pos-1) q.push(Node(now.l, now.pos-1));
if(now.pos+1 <= now.r) q.push(Node(now.pos+1, now.r));
}
for(register int i=1; i<=k; ++i) print(sta[i]), putchar(' ');
puts("");
}
inline void Solve(){
S.Build(1, 1, n);
while(m--){
int op = read(), l = read(), r = read(), x = read();
if(op == 1){ S.Modify(1, 1, n, l, r, x);}
if(op == 2){ int k = read(); Query(l, r, x, k);}
}
}
int main(){
n = read();
for(register int i=1; i<=n; ++i) a[i] = read();
m = read(); Solve();
return 0;
}