[Codeforces]849E Goodbye Souvenir

  又是一道比较新的模板题吧,即使是在Codeforces上小C还是贴了出来。

Description

  给定一个长度为n的序列a1~an,每个元素代表一种颜色。m次操作,每次操作为两种中的一种:

    1 p x:将第p个位置上的颜色修改为x;

    2 l r:询问[l,r]区间,求该区间内的每种颜色的“最大出现位置-最小出现位置”之和。

Input

  第一行两个正整数n、m;

  第二行n个整数,表示a1~an;

  接下来m行,每行表示一个如题所示的操作。

Output

  对于每个操作2,输出题目所求的答案。

Sample Input

  7 6
  1 2 3 1 3 2 1
  2 3 7
  2 1 3
  1 7 2
  1 3 2
  2 1 6
  2 5 7

Sample Output

  5
  0
  7
  1

HINT

  1 ≤ n,m ≤ 100 000,1≤ ai ≤ n;

  1 ≤ p,x ≤ n,1 ≤ l ≤ r ≤ n。

Solution

  应该说入手这道题还是很容易的,不管后面是怎么做,我们首先可以判定它是一道数据结构题。

  我们考虑对于每个元素,我们维护上一个出现它的颜色的位置。

  这样似乎就成为了我们很熟悉的矩形询问一类的问题。我们类比一下询问区间的颜色种数怎么做:

  第一维代表区间下标,第二维代表上一次出现该颜色的位置,要维护的信息是该位置出现的次数(其实只有0和1),目的是求和。

  同理这一题似乎同样可以这么做:

  第一维代表区间下标,第二维代表上一次出现该颜色的位置,要维护的信息是 区间下标与上一次出现的位置的差 ,目的是求和。

  这样似乎就很完美,我们可以直接树套树……然后并不能很爽地通过该题,因为炸空间了。

  那这可咋办呀,我们就可以用到我们神奇的分治算法——cdq分治!

  cdq算法的主要思想就是将操作区间分成两半,计算前一半操作对后一半询问的影响。

  这样就相当于将在线的修改去掉,将询问改为离线。

  这也就要求询问具有可合并性,如果操作之间会互相影响,cdq就不管用了。

  例如操作是加法而询问是取max,这样的询问是不满足可合并性的。

  对于这道题,每个操作对于答案的影响是独立的,且每次修改颜色都会改变至多6次我们所维护的信息:

  设pre[x]为上一次出现该color[x]的位置,suc[x]为下一次出现color[x]的位置。而我们只要维护pre[x]。

  假设修改pre[x],改之后的pre[x]为npre[x],suc[x]同理。

  要修改的所有信息为:pre[x],pre[suc[x]],pre[nsuc[x]]。

  对于每个信息在二维平面上的操作是一次单点减和一次单点加,所以总共是3*2=6次。

  完全转化成离线操作后,就只有询问矩形和了,把询问排序用一个普通线段树都是可以做的。

  每次操作出现在logm个分治区间里,单个操作的复杂度是logn,所以总时间复杂度O(mlogmlogn)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#define ll long long
#define MM 264005
#define MN 500005
using namespace std;
struct meg{int ki,pos,lf,rf,val,aps;}px[MN];
struct node{int g,x,y;}b[MM];
set <int> se[MM];
ll t[MM],ans[MM];
int c[MM][4],las[MM],pre[MM],col[MM];
int MQ,n,m,pxin;

inline int read()
{
    int n=0,f=1; char c=getchar();
    while (c<‘0‘ || c>‘9‘) {if(c==‘-‘)f=-1; c=getchar();}
    while (c>=‘0‘ && c<=‘9‘) {n=n*10+c-‘0‘; c=getchar();}
    return n*f;
}

inline void getadd(int x,int z) {for (x+=MQ;x;x>>=1) t[x]+=z;}
inline ll getsum(int x,int y)
{
    register ll lt=0;
    for (x+=MQ,y+=MQ;x<=y;x>>=1,y>>=1)
    {
        if ( x&1) lt+=t[x++];
        if (~y&1) lt+=t[y--];
    }
    return lt;
}

bool cmp(const meg& a,const meg& b) {return a.pos<b.pos || a.pos==b.pos && a.ki<b.ki;}
void solve()
{
    sort(px+1,px+pxin+1,cmp);
    register int i;
    for (i=1;i<=pxin;++i)
        if (px[i].ki==0) {if (px[i].lf) getadd(px[i].lf,px[i].val);}
        else ans[px[i].aps]+=getsum(px[i].lf,px[i].rf)*px[i].val;
    for (i=1;i<=pxin;++i) if (px[i].ki==0&&px[i].lf) getadd(px[i].lf,-px[i].val);
}

void work(int L,int R,int gs)
{
    if (!gs||L==R) return;
    int i,qet=0,mid=L+R>>1;
    pxin=0;
    for (i=L;i<=mid;++i)
        if (b[i].g==1)
        {
            px[++pxin]=(meg){0,b[i].x,c[i][0],0,c[i][0]-b[i].x,0};
            px[++pxin]=(meg){0,b[i].x,c[i][1],0,b[i].x-c[i][1],0};
            px[++pxin]=(meg){0,c[i][2],b[i].x,0,b[i].x-c[i][2],0};
            px[++pxin]=(meg){0,c[i][2],c[i][0],0,c[i][2]-c[i][0],0};
            px[++pxin]=(meg){0,c[i][3],c[i][1],0,c[i][1]-c[i][3],0};
            px[++pxin]=(meg){0,c[i][3],b[i].x,0,c[i][3]-b[i].x,0};
        }
    for (i=mid+1;i<=R;++i)
        if (b[i].g==2)
        {
            px[++pxin]=(meg){1,b[i].x-1,b[i].x,b[i].y,-1,i};
            px[++pxin]=(meg){1,b[i].y  ,b[i].x,b[i].y, 1,i};
            ++qet;
        }
    solve(); work(L,mid,gs-qet); work(mid+1,R,qet);
}

int main()
{
    register int i,x,qet=0;
    n=read(); m=read();
    pxin=0;
    for (MQ=1;MQ<n;MQ<<=1); --MQ;
    for (i=1;i<=n;++i)
    {
        col[i]=x=read();
        las[i]=pre[x]; pre[x]=i;
        se[x].insert(i);
        px[++pxin]=(meg){0,i,las[i],0,i-las[i],0};
    }
    for (i=1;i<=m;++i)
    {
        b[i].g=read(); b[i].x=read(); b[i].y=read();
        if (b[i].g==1)
        {
            set<int> ::iterator k;
            k=se[col[b[i].x]].lower_bound(b[i].x);
            if (k!=se[col[b[i].x]].begin())    --k,c[i][0]=*k,++k; else c[i][0]=0;
            if ((++k)!=se[col[b[i].x]].end()) c[i][2]=*k; else c[i][2]=0;
            se[col[b[i].x]].erase(--k);
            col[b[i].x]=b[i].y;
            k=se[col[b[i].x]].lower_bound(b[i].x);
            if (k!=se[col[b[i].x]].end()) c[i][3]=*k; else c[i][3]=0;
            if (k!=se[col[b[i].x]].begin()) c[i][1]=*(--k); else c[i][1]=0;
            se[col[b[i].x]].insert(b[i].x);
        }
        else
        {
            px[++pxin]=(meg){1,b[i].x-1,b[i].x,b[i].y,-1,i};
            px[++pxin]=(meg){1,b[i].y  ,b[i].x,b[i].y, 1,i};
            ++qet;
        }
    }
    solve(); work(1,m,qet);
    for (i=1;i<=m;++i) if (b[i].g==2) printf("%I64d\n",ans[i]);
}

Last Word

  感觉这题会让人觉得恶心的只有set的插入删除操作了。

  相比树套树,只需要用到普通线段树还是比较赏心悦目的。

时间: 2024-08-28 20:54:09

[Codeforces]849E Goodbye Souvenir的相关文章

[Codeforces]848C - Goodbye Souvenir

题目大意:n个数字,m次操作,支持修改一个数字和查询一个区间内每种数字最大出现位置减最小出现位置的和.(n,m<=100,000) 做法:把每个数字表示成二维平面上的点,第一维是在数组中的位置,第二维是在数组中前一个相同数字的位置,权值为这两个位置的差,询问等同于求矩形和,修改时会影响自己和相邻的相同数字,每种开一个set维护即可.矩形和可以用cdq分治,不容易被卡空间. 代码: #include<algorithm> #include<iostream> #include&

【Codeforces 848C】Goodbye Souvenir

Codeforces 848 C 题意:给\(n\)个数,\(m\)个询问,每一个询问有以下类型: 1 p x:将第p位改成x. 2 l r:求出\([l,r]\)区间中每一个出现的数的最后一次出现位置-第一次出现位置的和. 思路:我比较愚钝,只会最菜的\(O(n\times sqrt(n)\times log(n))\)的做法. 首先我们来想查询操作.我们将原序列分成B个一段,其中B是自己指定的.然后我们维护好所有的从第\(i\times B\)个到第\(j\times B-1\)个的数中的答

Codeforces Goodbye 2018

真是可怕...家里设备差的我无法想象...用txt打完的全场比赛[无奈] 先%下镇海ljz大佬和杭二lqz大佬 tql A题 签到题吧... 题面那么长,说到底就是求一个最大的 n + (n + 1) + (n + 2)的和 #include <bits/stdc++.h> using namespace std; int a,b,c; int main(){ scanf("%d%d%d",&a,&b,&c); b--;c-=2; int ans=m

Codeforces goodbye 2014 b

/**  * @brief good bye 2014 b  * @file b.cpp  * @author mianma  * @created 2014/01/06  10:20  * @edited  2014/01/06  10:20  * @type dfs greedy   * @note  */ #include <fstream> #include <iostream> #include <cstring> #include <vector>

Codeforces goodbye 2014 c

/**  * @brief good bye 2014 c  * @file c.cpp  * @author mianma  * @created 2014/01/06  16:42  * @edited  2014/01/06  16:42  * @type game  * @note   */ #include <fstream> #include <iostream> #include <cstring> #include <vector> #inc

CodeForces Goodbye 2017

传送门 A - New Year and Counting Cards •题意 有n张牌,正面有字母,反面有数字 其中元音字母$a,e,o,i,u$对应 原文地址:https://www.cnblogs.com/MMMinoz/p/11616206.html

CodeForces 337A Puzzles

Puzzles Time Limit: 1000ms Memory Limit: 262144KB This problem will be judged on CodeForces. Original ID: 337A64-bit integer IO format: %I64d      Java class name: (Any) The end of the school year is near and Ms. Manana, the teacher, will soon have t

codeforces 377A. Puzzles 水题

A. Puzzles Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/problemset/problem/337/A Description The end of the school year is near and Ms. Manana, the teacher, will soon have to say goodbye to a yet another class. She decided to p

【codeforces 718E】E. Matvey&#39;s Birthday

题目大意&链接: http://codeforces.com/problemset/problem/718/E 给一个长为n(n<=100 000)的只包含‘a’~‘h’8个字符的字符串s.两个位置i,j(i!=j)存在一条边,当且仅当|i-j|==1或s[i]==s[j].求这个无向图的直径,以及直径数量. 题解:  命题1:任意位置之间距离不会大于15. 证明:对于任意两个位置i,j之间,其所经过每种字符不会超过2个(因为相同字符会连边),所以i,j经过节点至多为16,也就意味着边数至多