[CF 1000F]One Occurrence
好前一段时间因为一些神必原因……博客放到了https://yahyachan.github.io。然后因为我回学校了所以接下来很长时间可能会继续用这个博客?
我谔谔,还是说这题……
对于询问\([l, r]\),考虑所有数在其中第一次出现的位置,如果说这个数在整个区间里只出现了一次,那么这个数下一次出现的位置一定大于\(r\)。
因此我们用扫描线搞一下就好啦……
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <functional>
#include <utility>
#include <vector>
using pii = std::pair<int, int>;
const int maxn = 500005;
const int maxno = maxn << 2;
pii maxv[maxno];
void maintain(int o) {
maxv[o] = std::max(maxv[o << 1], maxv[o << 1 | 1]);
}
void build_tree(int o, int L, int R) {
if(L == R) {
maxv[o] = std::make_pair(-1, -1);
} else {
int M = (L + R) / 2;
build_tree(o << 1, L, M);
build_tree(o << 1 | 1, M + 1, R);
maintain(o);
}
}
void modify(int o, int L, int R, const int &p, const pii &v) {
if(L == R) {
maxv[o] = v;
} else {
int M = (L + R) / 2;
if(p <= M) {
modify(o << 1, L, M, p, v);
} else {
modify(o << 1 | 1, M + 1, R, p, v);
}
maintain(o);
}
}
pii query(int o, int L, int R, const int &ql, const int &qr) {
if(ql <= L && R <= qr) {
return maxv[o];
} else {
int M = (L + R) / 2;
pii ret = std::make_pair(-100, -100);
if(ql <= M) ret = std::max(ret, query(o << 1, L, M, ql, qr));
if(qr > M) ret = std::max(ret, query(o << 1 | 1, M + 1, R, ql, qr));
return ret;
}
}
int rec[maxn], next[maxn];
int A[maxn];
std::vector<pii> que[maxn]; int ans[maxn];
int main() {
int n; scanf("%d", &n);
std::fill(rec, rec + maxn, n + 1);
for(int i = 1; i <= n; i ++) scanf("%d", &A[i]);
for(int i = n; i >= 1; i --) {
next[i] = rec[A[i]];
rec[A[i]] = i;
}
int q; scanf("%d", &q);
for(int i = 1; i <= q; i ++) {
int l, r; scanf("%d%d", &l, &r);
que[l].push_back({r, i});
}
build_tree(1, 1, n);
for(int i = 1; i <= 500000; i ++) {
if(rec[i] <= n) {
modify(1, 1, n, rec[i], {next[rec[i]], i});
}
}
for(int i = 1; i <= n; i ++) {
for(const pii &g : que[i]) {
int r = g.first, id = g.second;
pii tmp = query(1, 1, n, i, r);
if(tmp.first <= r) {
ans[id] = 0;
} else {
ans[id] = tmp.second;
}
}
int nx = next[i];
if(nx <= n) {
modify(1, 1, n, nx, {next[nx], A[i]});
}
}
for(int i = 1; i <= q; i ++) {
printf("%d\n", ans[i]);
}
return 0;
}
[BZOJ 3339]Rmq Problem
万恶的标题党啊~
这个题明显扫描线辣……但是怎么扫呢?
我们可以考虑对于每个前缀区间[tex][1,r][/tex]先求出他们的[tex]mex[/tex]值,然后用线段树维护。
从[tex][l,r][/tex]转移到[tex][l+1,r][/tex]时,需要将[tex][l,next[l]-1][/tex]([tex]next[l][/tex]是下一个和[tex]l[/tex]具有相同值的位置)中的所有大于[tex]A[l][/tex]([tex]l[/tex]的值)的值全部变为[tex]A[l][/tex]。但是这一步怎么做啊……难道要用Segment Beats?
答案是否定的。注意到我们真正需要的只是叶子结点,所以其他非叶子结点的最小值全部弄成无限大,然后这样做就可以把最小值当做标记来弄了……非常精巧。
代码:
/**************************************************************
Problem: 3339
User: danihao123
Language: C++
Result: Accepted
Time:1988 ms
Memory:10396 kb
****************************************************************/
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int maxn=200005,INF=0x7f7f7f7f;
const int maxnode=maxn*4;
int A[maxn];
int minv[maxnode];
void build_tree(int o,int L,int R){
if(L==R){
minv[o]=A[L];
}else{
int M=L+(R-L)/2;
minv[o]=INF;
build_tree(o<<1,L,M);
build_tree(o<<1|1,M+1,R);
}
}
inline void paint(int o,int v){
minv[o]=min(minv[o],v);
}
inline void pushdown(int o){
paint(o<<1,minv[o]);
paint(o<<1|1,minv[o]);
minv[o]=INF;
}
int pos;
int query(int o,int L,int R){
if(L==R)
return minv[o];
if(minv[o]<INF)
pushdown(o);
int M=L+(R-L)/2;
if(pos<=M)
return query(o<<1,L,M);
else
return query(o<<1|1,M+1,R);
}
int ql,qr,v;
void update(int o,int L,int R){
if(ql<=L && R<=qr){
paint(o,v);
}else{
if(minv[o]<INF)
pushdown(o);
int M=L+(R-L)/2;
if(ql<=M)
update(o<<1,L,M);
if(qr>M)
update(o<<1|1,M+1,R);
}
}
inline int readint(){
register int x=0;
register char c;
fread(&c,1,1,stdin);
while(!isdigit(c))
fread(&c,1,1,stdin);
while(isdigit(c)){
x=x*10+c-'0';
fread(&c,1,1,stdin);
}
return x;
}
int buf[21];
inline void putint(int x){
register int i,p=0;
if(!x){
buf[p++]=0;
}else{
while(x){
buf[p++]=x%10;
x/=10;
}
}
for(i=p-1;i>=0;i--)
putchar(buf[i]+'0');
putchar('\n');
}
bool vis[maxn];
int last[maxn],next[maxn];
struct Query{
int id;
int l,r;
int ans;
};
bool cmp1(const Query& x,const Query& y){
if(x.l==y.l)
return x.r<y.r;
else
return x.l<y.l;
}
bool cmp2(const Query& x,const Query& y){
return x.id<y.id;
}
Query QS[maxn];
int V[maxn];
int main(){
int n,q;
register int i,l=1,k=0;
n=readint();
q=readint();
for(i=1;i<=n;i++){
V[i]=readint();
vis[V[i]]=true;
if(k==V[i])
while(vis[k])
k++;
A[i]=k;
}
build_tree(1,1,n);
for(i=n;i>=1;i--){
if(!last[V[i]])
next[i]=n+1;
else
next[i]=last[V[i]];
last[V[i]]=i;
}
for(i=1;i<=q;i++){
QS[i].id=i;
QS[i].l=readint();
QS[i].r=readint();
}
sort(QS+1,QS+1+q,cmp1);
for(i=1;i<=q;i++){
while(l<QS[i].l){
ql=l;
qr=next[l]-1;
v=V[l];
update(1,1,n);
l++;
}
pos=QS[i].r;
QS[i].ans=query(1,1,n);
}
sort(QS+1,QS+1+q,cmp2);
for(i=1;i<=q;i++){
putint(QS[i].ans);
}
return 0;
}
[BZOJ 1878]HH的项链
扫描线处女作TAT
直接离线,按左端点排序扫描。每个点要保存后继相同节点,每种元素第一次出现的位置都加1。然后扫描的时候有后继就给后继加。之后求区间和即可。
代码:
/**************************************************************
Problem: 1878
User: danihao123
Language: C++
Result: Accepted
Time:1344 ms
Memory:8444 kb
****************************************************************/
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int maxn=50001,maxm=200001;
int T[maxn];
int n;
inline int lowbit(int x){
return x&(-x);
}
inline void update(int p,int v){
while(p<=n){
T[p]+=v;
p+=lowbit(p);
}
}
inline int query(int p){
register int ans=0;
while(p>0){
ans+=T[p];
p-=lowbit(p);
}
return ans;
}
struct Query{
int l,r;
int id;
int ans;
bool operator <(const Query& x) const{
return l==x.l?r<x.r:l<x.l;
}
};
Query Q[maxm];
bool cmp2(const Query& a,const Query& b){
return a.id<b.id;
}
// I/O优化
inline int readint(){
char c=getchar();
register int x=0;
while(!isdigit(c))
c=getchar();
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return x;
}
int bf[10];
inline void writeint(int x){
register int p=0;
if(x==0){
bf[p++]=0;
}else{
while(x){
bf[p++]=x%10;
x/=10;
}
}
for(register int i=p-1;i>=0;i--)
putchar('0'+bf[i]);
}
int next[maxn];
int A[maxn];
int pos[1000001];
int main(){
int m,maxA=0;
register int i,j;
n=readint();
for(i=1;i<=n;i++){
A[i]=readint();
maxA=max(maxA,A[i]);
}
for(i=n;i;i--){
next[i]=pos[A[i]];
pos[A[i]]=i;
}
for(i=1;i<=maxA;i++)
if(pos[i])
update(pos[i],1);
m=readint();
for(i=1;i<=m;i++){
Q[i].l=readint();
Q[i].r=readint();
Q[i].id=i;
}
sort(Q+1,Q+1+m);
register int tot=1;
for(i=1;i<=m;i++){
while(tot<Q[i].l){
if(next[tot])
update(next[tot],1);
tot++;
}
Q[i].ans=query(Q[i].r)-query(Q[i].l-1);
}
sort(Q+1,Q+1+m,cmp2);
for(i=1;i<=m;i++){
writeint(Q[i].ans);
putchar('\n');
}
return 0;
}