[Codeforces]848C - Goodbye Souvenir

题目大意:n个数字,m次操作,支持修改一个数字和查询一个区间内每种数字最大出现位置减最小出现位置的和。(n,m<=100,000)

做法:把每个数字表示成二维平面上的点,第一维是在数组中的位置,第二维是在数组中前一个相同数字的位置,权值为这两个位置的差,询问等同于求矩形和,修改时会影响自己和相邻的相同数字,每种开一个set维护即可。矩形和可以用cdq分治,不容易被卡空间。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<queue>
#include<map>
#include<set>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
#define MN 100000
#define N 131072
set<int> s[MN+5];
int a[MN+5],ls[MN+5],wn,qn,pn;
long long ans[MN+5],t[N*2+5];
struct work{int t,a,b,c;}w[MN*7+5];
struct P{int t,a,b,c,d;}p[MN*14+5];
bool cmp(const P&a,const P&b){return a.a==b.a?a.t<b.t:a.a<b.a;}
void add(int k,int x){for(k+=N;k;k>>=1)t[k]+=x;}
long long query(int l,int r)
{
    long long res=0;
    for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1)res+=t[l+1];
        if( r&1)res+=t[r-1];
    }
    return res;
}
void solve(int l,int r)
{
    int mid=l+r>>1,i;
    if(l<r)solve(l,mid),solve(mid+1,r);
    for(pn=0,i=l;i<=mid;++i)if(w[i].t<2)p[++pn]=(P){0,w[i].a,w[i].b,w[i].c,0};
    for(;i<=r;++i)if(w[i].t>1)p[++pn]=(P){1,w[i].a-1,w[i].a,w[i].b,w[i].c},
                              p[++pn]=(P){2,w[i].b,w[i].a,w[i].b,w[i].c};
    sort(p+1,p+pn+1,cmp);
    for(i=1;i<=pn;++i)
    {
        if(p[i].t==0)add(p[i].b,p[i].c);
        if(p[i].t==1)ans[p[i].d]-=query(p[i].b,p[i].c);
        if(p[i].t==2)ans[p[i].d]+=query(p[i].b,p[i].c);
    }
    for(i=1;i<=pn;++i)if(p[i].t==0)add(p[i].b,-p[i].c);
}
int main()
{
    int n,m,i,t,x,y;
    n=read();m=read();
    for(i=1;i<=n;s[a[i]].insert(ls[a[i]]=i),++i)
        if(ls[a[i]=read()])w[++wn]=(work){1,i,ls[a[i]],i-ls[a[i]]};
    for(i=1;i<=m;++i)
    {
        t=read();x=read();y=read();
        if(t==1)
        {
            set<int>::iterator i=s[a[x]].find(x),j=i,k=i;++k;
            if(i!=s[a[x]].begin())
            {
                --j;w[++wn]=(work){1,x,*j,*j-x};
                if(k!=s[a[x]].end())w[++wn]=(work){1,*k,*j,*k-*j};
            }
            if(k!=s[a[x]].end())w[++wn]=(work){1,*k,x,x-*k};
            s[a[x]].erase(i);
            s[a[x]=y].insert(x);
            i=j=k=s[y].find(x);++k;
            if(i!=s[y].begin())
            {
                --j;w[++wn]=(work){1,x,*j,x-*j};
                if(k!=s[y].end())w[++wn]=(work){1,*k,*j,*j-*k};
            }
            if(k!=s[y].end())w[++wn]=(work){1,*k,x,*k-x};
        }
        else w[++wn]=(work){2,x,y,++qn};
    }
    solve(1,wn);
    for(i=1;i<=qn;++i)printf("%I64d\n",ans[i]);
}
时间: 2024-08-03 04:33:10

[Codeforces]848C - Goodbye Souvenir的相关文章

[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,输出题目所求的答案. Sam

【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,也就意味着边数至多