首页 > 其他分享 >CF EDU165-E-序列问题,线段树

CF EDU165-E-序列问题,线段树

时间:2024-05-01 09:04:09浏览次数:20  
标签:EDU165 int 线段 tr CF mid lst ql pst

link:https://codeforces.com/contest/1969/problem/E

给一序列 \(a\),要使得 \(a\) 的任意子段 \([a_l,\dots,a_r]\) 都存在某数 \(a_i\),使得其只在该子段恰出现一次。问最少修改 \(a\) 中几处位置?
\(1\leq n\leq 3\times 10^5\).


一个不太好的想法:对每个值去考虑,这样的入手点只考虑了局部的某个值,而无法联系上整个序列这一整体结构。

这个问题应该抓的重点是其整体结构:假设 \([a_1,\dots,a_i]\) 的所有子段都满足条件,考虑加入一个 \(a_{i+1}\) 产生的的影响,只需考虑所有以 \(i+1\) 为右端点的区间是否仅含一个数。
那么很自然地考察这些后缀区间,所有以 \(i\) 为右端点的区间,如果因为在结尾拼上了一个 \(a_{i+1}\) 而“变坏了”,那罪魁祸首只可能是 \(a_{i+1}\) 它和之前某个数(不妨称其为 \(a_j\) )重复了,并且\(a_j\)是作为 \([j,i+1]\) 中唯一一个仅出现一次的数

好,那现在就是怎么快速判断它的问题,一个自然的想法是在这时候对每个值考虑:一个值出现1次到出现2次是质变,而2次以上其实我们并不关心,因此如果动态地对每个值打个记号,在最后一次出现的位置打上 +1,倒数第二次出现的位置打 -1,更前的位置全部打 0,记录这样一个序列 \([s_1,\dots,s_n]\),那么就变成判断,是否存在某个 \([s_l,\dots,s_{i+1}]\) 的和为0。

考虑设 \(f_i=\sum_{j=i}^n s_j\) 这样一个后缀和,那么判断的是 \(\min(f_1,\dots,f_{i+1})>0\),而对于插入 \(a_{i+1}\) 而言,意味着修改 \(s_{i+1}\),也就是对所有 \(f_1,\dots,f_{i+1}\) 做一个区间修改,所以就是区间min-区间赋值(具体地应该是区间加)的问题,线段树可以解决:

(写的时候把 \(\min\) 打成了 \(\max\) 还调了半天T_T)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=3e5+5;
const int INF=0x3f3f3f3f;
int n,a[N],s[N];
int lst[N],pst[N];
struct SegT{
    #define ls (node<<1)
    #define rs (node<<1|1)
    int n;
    int tr[N<<2],tag[N<<2];
    void push_up(int node){
        tr[node]=min(tr[ls],tr[rs]);
    }
    void push_down(int node){
        if(tag[node]==0)return;
        tag[ls]+=tag[node];tr[ls]+=tag[node];
        tag[rs]+=tag[node];tr[rs]+=tag[node];
        tag[node]=0;
    }
    void build(int node,int l,int r){
        tr[node]=tag[node]=0;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(ls,l,mid);build(rs,mid+1,r);
    }
    void init(int __n){
        n=__n;
        build(1,1,n);
    }
    void add(int node,int l,int r,int ql,int qr,int v){
        if(ql<=l&&r<=qr){
            tag[node]+=v;
            tr[node]+=v;
            return;
        }
        push_down(node);
        int mid=(l+r)>>1;
        if(mid>=ql)add(ls,l,mid,ql,qr,v);
        if(mid+1<=qr)add(rs,mid+1,r,ql,qr,v);
        push_up(node);
    }
    int query(int node,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return tr[node];
        push_down(node);
        int ret=INF;
        int mid=(l+r)>>1;
        if(mid>=ql)ret=query(ls,l,mid,ql,qr);
        if(mid+1<=qr)ret=min(ret,query(rs,mid+1,r,ql,qr));
        return ret;
    }
    int query(int l,int r){return query(1,1,n,l,r);}
    void modify(int x,int v){
        add(1,1,n,1,x,-s[x]);
        s[x]=v;
        add(1,1,n,1,x,s[x]);
    }
}tr;
int main(){
    fastio;
    int tc;cin>>tc;
    while(tc--){
        cin>>n;
        rep(i,1,n)cin>>a[i];
        rep(i,1,n)lst[i]=pst[i]=-1,s[i]=0;
        tr.init(n);
        int ans=0;
        rep(i,1,n){
            if(~pst[a[i]])tr.modify(pst[a[i]],0);
            if(~lst[a[i]])tr.modify(lst[a[i]],-1);
            tr.modify(i,1);
            
            if(tr.query(1,i)>0){
                pst[a[i]]=lst[a[i]];
                lst[a[i]]=i;
                continue;
            }

            ans++;
            if(~lst[a[i]])tr.modify(lst[a[i]],1);
            if(~pst[a[i]])tr.modify(pst[a[i]],-1);
        }
        cout<<ans<<endl;
    }
    return 0;
}

标签:EDU165,int,线段,tr,CF,mid,lst,ql,pst
From: https://www.cnblogs.com/yoshinow2001/p/18168981

相关文章

  • CF EDU164-E-数论分块
    link:https://codeforces.com/contest/1954/problem/E有一排怪物,第\(i\)只有\(a_i\)的血,每次攻击可以选择在\(i\)处放一个技能,技能会一直向左/右以相同的\(k\)点伤害扩散,直至遇到边界或已经死亡的怪物。问最少需要放几次技能?需要对所有\(k\)回答答案。\(n,a_i\leq10^......
  • 「CF1017G」The Tree
    这是一道非常NB的题意转化题.Introduction给定一棵树,维护以下3个操作:1x表示如果节点\(x\)为白色,则将其染黑.否则对这个节点的所有儿子递归进行相同操作;2x表示将以节点\(x\)为\(root\)的子树染白;3x表示查询节点\(x\)的颜色.StrikingIdeas修改复......
  • CF1765C Card Guessing 题解
    考虑期望的线性性,求每种情况猜对的概率和,最终再除掉\({4n\choosen,n,n,n}\)。考虑枚举最少的出现次数\(mn\),记四种卡的出现次数分别为\(c_1,c_2,c_3,c_4\),\(c_1+c_2+c_3+c_4=i\lek\),则这种情况的方案数为:\[{i\choosec_1,c_2,c_3,c_4}{4n-i\choosen-c_1,n-c_2,n-c_3,n-c_......
  • 题解 CF1965E【Connected Cubes】
    场切了1E,第一次上IGM,纪念一下。多图警告。我们称题目中的一个方块为“某色混凝土”。感受一下,发现本题主要的难点在于这些混凝土方块排布得太紧密了,导致容易出现互相遮挡的现象,进而难以构造。于是,我们先思考能否通过一些操作使得这些混凝土互相分离。如下图的方式可以将每两......
  • CF1716E 某种神秘矩阵做法
    闲话我和@AcaCaca_duel,然后我写了如下的神奇做法,然后vector疯狂CE,爆了。为什么没人像我这样做啊喂!看来还是我太菜了题解首先众所周知的,序列最大子段和可以用\(\max+\)矩阵来做。考虑一个翻转,其实就是在从下往上递归中某一层所有相邻的两个矩阵进行了交换,换句话说,从左......
  • CF1956F Nene and the Passing Game 题解
    题目链接点击打开链接题目解法首先答案为连边之后连通块的个数把限制中的\(i,j\)分开,则\(i,j\)能传球当且仅当\(i+l_i\lej-l_j\)且\(j-r_j\lei+r_i\)这是一个二维偏序的形式先考虑怎么暴力做,考虑将\((i+l_i,0),\;(i-l_i,1)\)排序,然后按顺序加入如果碰到操作\(......
  • CF1951I Growing Trees
    MyBlogsCF1951IGrowingTrees首先考虑确定了\(x_i\)如何判定是否合法。可以很容易的找出这样一个必要条件:\(\foralli,x_i\leqk\)。这是两个点的情况,考虑点数更多的情况。手玩之后可以推广到:对于任意导出子图,要求其内部的边数\(\leqk(|S|-1)\)。这个条件也是充分的,证明......
  • 题解:CF1957A Stickogon
    CF1957AStickogon题意题意十分简单,给予你\(n\)个棍子,问这些棍子可以构成多少个正多边形。思路说是可以构成多少个正多边形,所以我们可以用边最少的正多边形等边三角形来计数。在输入\(a\)的时候,用一个数组\(f\)来计算\(a\)出现的次数,当\(f_{a}\)等于\(3\)时,答案......
  • CF1966D Missing Subsequence Sum 题解
    题意:给定\(n(n\le10^6)\)和\(k(k\len)\)。构造一个长度小于等于\(25\)的序列\(a\)满足:1.不存在一个子序列的和为\(k\)。2.对于\(1\lei\len,i\nek\),存在一个子序列的和为\(i\)。看到长度为\(25\),首先肯定会想到二进制。那么我们先构造出一个序列\([2^......
  • CF1842H Tenzing and Random Real Numbers 题解
    题目链接点击打开链接题目解法实数的概率好反直觉!对\(1\)做限制不是很好做,考虑变成正负性的限制(即对\(0\)做限制)令\(y_i=x_i-0.5\),那么限制就变成了\(y_i+y_j\le0,\;y_i+y_j\ge0\)这里要给出一些实数概率的结论:实数下,\(x=y\)的概率为\(0\),因为\(\frac{1}{\inft......