[Avito Code Challenge 2018 G] Magic multisets(线段树)

题目链接:http://codeforces.com/contest/981/problem/G

题目大意:

有n个初始为空的‘魔法’可重集,向一个‘可重集’加入元素时,若该元素未出现过,则将其加入;否则该可重集中所有元素的个数都会翻倍。

例如将$2$加入${1,3}$会得到${1,2,3}$,将$2$加入${1,2,3,3}$会得到${1,1,2,2,3,3,3,3}$.

$q$次操作,每次操作要么向一个区间内的所有可重集加入某个元素,要么询问一个区间内可重集的大小之和。

$n,q ≤ 2×10^5$

题解:

发现对于出现过该元素的区间就是区间乘,没有出现过的就是区间加

操作1我们先把$[l,r]$全部乘上2,再把之前已经出现过当前元素的区间乘上2的逆元再+1,最后合并一下左右区间就好了。合并大概就是保证这个操作时间复杂度的关键了,只是我也不知道怎么算

发现这样我们要维护每个元素出现的区间,同时支持方便的合并,我们开n个set就好了

正是做完这题我发现我竟然不会写区间乘法线段树

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<set>
#define pa pair<int,int>
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;

const int N=2e5+15;
const int mod=998244353;
ll n,q,inv;
ll sum[N<<2],add[N<<2],mul[N<<2];
set <pa> st[N];
inline ll read()
{
    char ch=getchar();
    ll s=0,f=1;
    while (ch<‘0‘||ch>‘9‘) {if (ch==‘-‘) f=-1;ch=getchar();}
    while (ch>=‘0‘&&ch<=‘9‘) {s=(s<<3)+(s<<1)+ch-‘0‘;ch=getchar();}
    return s*f;
}
ll qpow(ll a,ll b)
{
    ll re=1;
    for (;b;b>>=1,a=a*a%mod) if (b&1) re=re*a%mod;
    return re;
}
void build(int o,int l,int r)
{
    mul[o]=1;add[o]=sum[o]=0;
    if (l==r) return;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
}
void pushup(int o,int l,int r)
{
    //sum[o]=sum[o<<1]+sum[o<<1|1];
    sum[o]=(sum[o<<1]*mul[o<<1]+add[o<<1]*(mid-l+1))%mod;
    sum[o]=(sum[o]+sum[o<<1|1]*mul[o<<1|1]+add[o<<1|1]*(r-mid))%mod;
}
void pushdown(int o,int l,int r)
{
    if (mul[o]!=1)
    {
        ll p=mul[o];
        mul[o]=1;
        //(sum[o<<1]*=p)%=mod;
        //(sum[o<<1|1]*=p)%=mod;
        (add[o<<1]*=p)%=mod;
        (add[o<<1|1]*=p)%=mod;
        (mul[o<<1]*=p)%=mod;
        (mul[o<<1|1]*=p)%=mod;
    }
    if (add[o]!=0)
    {
        ll p=add[o];
        add[o]=0;
    //  (sum[o<<1]+=p*(mid-l+1))%=mod;
    //    (sum[o<<1|1]+=p*(r-mid))%=mod;
        (add[o<<1]+=p)%=mod;
        (add[o<<1|1]+=p)%=mod;
    }
}
void update(int o,int l,int r,int x,int y,ll z,int flag)
{
    if (l>=x&&r<=y)
    {
        if (flag==1)
        {
            (mul[o]*=z)%=mod;
            (add[o]*=z)%=mod;
        //    (sum[o]*=z)%=mod;
        }
        if (flag==2)
        {
            (add[o]+=z)%=mod;
        //    (sum[o]+=(r-l+1)*z)%=mod;
        }
        return;
    }
    pushdown(o,l,r);
    if (x<=mid) update(o<<1,l,mid,x,y,z,flag);
    if (y>mid) update(o<<1|1,mid+1,r,x,y,z,flag);
    pushup(o,l,r);
}
void merge(int x,int L,int R)
{
    set<pa>::iterator it;
    it=st[x].lower_bound(pa(L,L));
    for (;it!=st[x].end();it++)
    {
        set<pa>::iterator lst=it;lst--;
        int l=(*lst).second+1;
        int r=(*it).first-1;
        int upl=max(L,l);
        int upr=min(R,r);
        if (upr>=upl)
        {
            update(1,1,n,upl,upr,inv,1);
            update(1,1,n,upl,upr,1,2);
        }
        if ((*it).first>=R) break;
    }
    int mergeL=L,mergeR=R;
    it=st[x].upper_bound(pa(L,L));it--;
    if ((*it).second>=mergeL) mergeL=(*it).first;
    it=st[x].upper_bound(pa(R,R));it--;
    if ((*it).second>=mergeR) mergeR=(*it).second;
    vector <pa> er;
    it=st[x].lower_bound(pa(mergeL,mergeL));
    for (;it!=st[x].end();it++)
    {
        pa e=*it;
        if (e.first>=mergeL&&e.second<=mergeR) er.push_back(e);
        else break;
    }
    for (int i=0;i<er.size();i++) st[x].erase(er[i]);
    st[x].insert(pa(mergeL,mergeR));
}
ll query(int o,int l,int r,int x,int y)
{
    if (l>=x&&r<=y) return (sum[o]*mul[o]+add[o]*(r-l+1))%mod;
//    if (l>=x&&r<=y) return sum[o]%mod;
    pushdown(o,l,r);
    ll re=0;
    if (x<=mid) (re+=query(o<<1,l,mid,x,y))%=mod;
    if (y>mid) (re+=query(o<<1|1,mid+1,r,x,y))%=mod;
    pushup(o,l,r);
    return re;
}
int main()
{
    inv=qpow(2,mod-2);
    //inv=(mod+1)/2;
    n=read();q=read();
    for (int i=0;i<=n;i++)
    {
        st[i].insert(pa(0,0));
        st[i].insert(pa(n+1,n+1));
    }
    build(1,1,n);
    while (q--)
    {
        int opt=read();
        if (opt==1)
        {
            int l=read(),r=read(),z=read();
            update(1,1,n,l,r,2,1);
            merge(z,l,r);
        }
        if (opt==2)
        {
            int l=read(),r=read();
            printf("%lld\n",query(1,1,n,l,r));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/xxzh/p/9758635.html

时间: 2024-10-09 22:36:54

[Avito Code Challenge 2018 G] Magic multisets(线段树)的相关文章

Codeforces Avito Code Challenge 2018 D. Bookshelves

Codeforces Avito Code Challenge 2018 D. Bookshelves 题目连接: http://codeforces.com/contest/981/problem/D Description Mr Keks is a typical white-collar in Byteland. He has a bookshelf in his office with some books on it, each book has an integer positive

cf掉分记——Avito Code Challenge 2018

再次作死的打了一次cf的修仙比赛感觉有点迷.. 还好掉的分不多(原本就太低没法掉了QAQ) 把会做的前三道水题记录在这.. A: Antipalindrome emmmm...直接暴力枚举 code: //By Menteur_Hxy #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; int n,ans; cha

HDU 5125 magic balls(线段树+DP)

magic balls Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 323    Accepted Submission(s): 90 Problem Description The town of W has N people. Each person takes two magic balls A and B every day.

【bzoj4276】[ONTAK2015]Bajtman i Okr?g?y Robin 线段树优化建图+费用流

题目描述 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢走c[i]元.作为保安,你在每一段长度为1的时间内最多只能制止一个强盗,那么你最多可以挽回多少损失呢? 输入 第一行包含一个正整数n(1<=n<=5000),表示强盗的个数. 接下来n行,每行包含三个正整数a[i],b[i],c[i](1<=a[i]<b[i]<=5000,1<=c[i]

Avito Cool Challenge 2018 B - Farewell Party

题目大意: 有n个人 接下来一行n个数a[i] 表示第i个人描述其他人有a[i]个的帽子跟他不一样 帽子编号为1~n 如果所有的描述都是正确的 输出possible 再输出一行b[i] 表示第i个人的帽子的编号 如果存在矛盾 输出impossible 如果存在p 个人都描述有q个人跟他们的帽子不一样 此时若 p+q=n 说明正确且这p个人的帽子都一样 如 a[] = 3 3 2 2 2 ,此时一种解为 b[] = 1 1 2 2 2 存在p=2个人描述有q=3个人跟他们不一样 说明这两个人的帽子

Avito Cool Challenge 2018:C. Colorful Bricks

C. Colorful Bricks 题目链接:https://codeforces.com/contest/1081/problem/C 题意: 有n个横向方块,一共有m种颜色,然后有k个方块的颜色与其左边的颜色不同(第一个除外),问一共有多少染色方案. 题解: 我们首先来考虑一下dp. 设dp(i,j)为当前第i个方块,一共有j个方块与它前面的方块不同的方案个数. 那么转移方程为dp(i,j)=dp(i-1,j-1)*(m-1)+dp(i-1,j). 代码如下: #include <bits

Avito Cool Challenge 2018 A. B题解

A. Definite Game 题目链接:https://codeforces.com/contest/1081/problem/A 题意: 给出一个数v,然后让你可以重复多次减去一个数d,满足v%d!=0,问最后可以得到最小的是多少. 题解: 除开v=2输出2,其余直接输出1就行了= =/ 代码如下: #include <bits/stdc++.h> using namespace std; int main(){ int v; cin>>v; cout<<(v==

Avito Cool Challenge 2018:D. Maximum Distance (最小生成树)

题目链接 题意 : 给出一个联通图和一些特殊的点,现在定义cost(u,v)为一条从u到v的路径上面边权的最大值 , 定义dis(u,v) 为从u到v 路径上面cost 的最小值 然后求所有特殊点到其他特殊点的最大距离 题解: 做这题前,首先思考一件事情,对于一颗树来说点到点的距离是不是就是树上面路径的边权最大值 我们来证明一下:假设在最小生成树上面的路径cost为w1,另外在原图中还有一条路径从u到v,其cost为w2,那么必然有w2>w1的.那么我们最后的dis一定是w1. 那么我们现在的目

Avito Cool Challenge 2018 Solution

A. Definite Game 签. 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 int a; 7 while (scanf("%d", &a) != EOF) 8 { 9 [](int x) 10 { 11 for (int i = x - 1; i >= 1; --i) if (x % i) 12 { 13 printf("%d\n",