【Luogu】P2221高速公路(线段树乱搞)

  题目链接

  这题……我从一开始就想歪了qwq。

  为了缅怀逝去的一小时我就把我的30分暴力思路放上来。

  首先我们观察枚举的区间。假设我们要枚举的范围是1~5之间的四条路,为了方便我们把它们叫做abcd。

  那么观察我们枚举的区间。

  a  ab  abc  abcd  b  bc  bcd  c  cd  d

  观察有一些区间是可以合起来的。

  然后观察a出现4次,b出现6次,c出现6次,d出现4次。

  是有一定规律的qwq

  然后就$\frac{nm}{2}的复杂度搞搞  就三十分

  正确思路是,观察一条路选不选上(设这条路左点x):左区间在(l,x),右区间在(x+1,r)

  这些区间会把这条路选上。

  于是这条路选上的次数就等于(x-l+1)(r-x)s[x]

  化简得到x*s[x]、s[x]、x^2*s[x]三种可以用线段树维护的东西

  然后线段树搞

  

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cstdlib>
#define left (rt<<1)
#define right (rt<<1|1)
#define mid ((l+r)>>1)
#define lson l,mid,left
#define rson mid+1,r,right
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch==‘-‘)    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-‘0‘;
        ch=getchar();
    }
    return num*f;
}

long long gcd(long long a,long long b){    return b==0?a:gcd(b,a%b);    }

long long sum[1000010];
long long mul[1000010];
long long tree[1000010];
long long sree[1000010];
long long mree[1000010];
long long tag[1000010];

inline void pushup(int rt){
    tree[rt]=tree[left]+tree[right];
    sree[rt]=sree[left]+sree[right];
    mree[rt]=mree[left]+mree[right];
}

void pushdown(int rt,int l,int r){
    if(tag[rt]==0)    return;
    tag[left]+=tag[rt];
    tag[right]+=tag[rt];
    tree[left]+=tag[rt]*((r-l+1)-((r-l+1)>>1));
    tree[right]+=tag[rt]*((r-l+1)>>1);
    sree[left]+=tag[rt]*(sum[mid]-sum[l-1]);
    sree[right]+=tag[rt]*(sum[r]-sum[mid]);
    mree[left]+=tag[rt]*(mul[mid]-mul[l-1]);
    mree[right]+=tag[rt]*(mul[r]-mul[mid]);
    tag[rt]=0;
}

void update(int from,int to,long long num,int l,int r,int rt){
    if(from<=l&&to>=r){
        tag[rt]+=num;
        tree[rt]+=num*(r-l+1);
        sree[rt]+=num*(sum[r]-sum[l-1]);
        mree[rt]+=num*(mul[r]-mul[l-1]);
        return;
    }
    pushdown(rt,l,r);
    if(from<=mid)    update(from,to,num,lson);
    if(to>mid)        update(from,to,num,rson);
    pushup(rt);
}

struct Ans{
    long long x,y,z;
    void clear(){x=y=z=0;}
    Ans operator +(const Ans a){
        Ans ans=(Ans){x+a.x,y+a.y,z+a.z};
        return ans;
    }
};

Ans query(int from,int to,int l,int r,int rt){
    if(from<=l&&to>=r)    return(Ans){tree[rt],sree[rt],mree[rt]};
    pushdown(rt,l,r);
    Ans opt; opt.clear();
    if(from<=mid)    opt=query(from,to,lson);
    if(to>mid)        opt=opt+query(from,to,rson);
    return opt;
}

int main(){
    int n=read(),m=read();
    for(int i=1;i<=n;++i){
        sum[i]=sum[i-1]+i;
        mul[i]=mul[i-1]+1LL*i*i;
    }
    for(int i=1;i<=m;++i){
        char ch[10];
        scanf("%s",ch);int x=read(),y=read();
        if(ch[0]==‘C‘){
            long long z=read();
            update(x,y-1,z,1,n-1,1);
        }
        else{
            Ans now=query(x,y-1,1,n-1,1);
            long long ans=0;
            ans+=now.y*(long long)(x+y-1);
            ans+=now.x*(long long)(1LL*y-1LL*x*y);
            ans-=now.z;
            long long len=y-x+1;
            long long cnt=len*(len-1)/2;
            long long gc=gcd(ans,cnt);
            ans/=gc; cnt/=gc;
            printf("%lld/%lld\n",ans,cnt);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cellular-automaton/p/8214962.html

时间: 2024-10-08 13:07:03

【Luogu】P2221高速公路(线段树乱搞)的相关文章

JZYZOJ1527 [haoi2012]高速公路 线段树 期望

http://172.20.6.3/Problem_Show.asp?id=1527 日常线段树的pushdown写挂,果然每次写都想得不全面,以后要注意啊--求期望部分也不熟练,和平均数搞混也是orz,我已经是个期望都求不出来的废人了.这道题显然(大概)每个段的贡献是val[i]*(y-i+1)*(i-x+1);整体来说算是一看就是线段树的题. 代码 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring>

[Luogu] 可持久化线段树 1(主席树)

https://www.luogu.org/problemnew/show/P3834 #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn = 2e5 + 10; #define RR freopen("gg.in", "r", stdin) int n

luogu 1712 区间(线段树+尺取法)

题意:给出n个区间,求选择一些区间,使得一个点被覆盖的次数超过m次,最小的花费.花费指的是选择的区间中最大长度减去最小长度. 坐标值这么大,n比较小,显然需要离散化,需要一个技巧,把区间转化为半开半闭区间,然后线段树的每一个节点表示一个半开半闭区间. 接着我们注意到需要求最小的花费,且这个花费只与选择的区间集合中的最大长度和最小长度有关. 这意味着如果最大长度和最小长度一定,我们显然是需要把中间长度的区间尽量的选择进去使答案不会变的更劣. 不妨把区间按长度排序,枚举每个最小长度区间,然后最大区间

[bzoj 1798][luogu p2023]Seq 线段树Seq

题目大意: 维护一个数列,支持区间乘,区间加,求区间和. 线段树题,对于乘和加操作我们可以维护一个标记.对于乘用乘法分配律分解. 代码如下: 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 const int N = 100010; 7 int inline getint() 8 { 9

【BZOJ-4523】路由表 Trie树 + 乱搞

4523: [Cqoi2016]路由表 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 155  Solved: 98[Submit][Status][Discuss] Description 路由表查找是路由器在转发IP报文时的重要环节.通常路由表中的表项由目的地址.掩码.下一跳(Next Hop)地址和其他辅助信息组成.例如: 当路由器收到一个IP报文时,会将报文中的目的IP地址与路由表中的表项逐条进行比较,选择匹配且最明确的表项,将报文转发给

NOJ1560---Let Slimes Grow Up(线段树)

问题描述 You know 8Mao has his own Slime Team. But he soon found that let the Slimes stand in a line and make them from low to high is a such stupid thing since the Slimes are so f[bi][bi]king stupid! As a result, 8Mao decided to give up. But 8Mao still

ZOJ 5332 Calculation(离线 + 线段树)

题目链接 :http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5332 比赛的时候没有做出来,赛后看了官方题解,虽然各种orz但是依旧只能orz(标程写得真是犀利),然后可耻的到网上找了下题解... 做法是线段树 + 离线搞, 网上那种对于[l, r]中的l从大到小排序很精妙(有一篇竟然说是以r为关键字,,,然后代码里面却是l...汗), 再注意到gcd()对于每一个起点来说,是单调递减的,这样可以乱搞了... 如果还不是很明白

hdu 4902 线段树+逆向模拟

http://acm.hdu.edu.cn/showproblem.php?pid=4902 出n个数,然后对这n个数进行两种操作: 如果是 1 l r x,则把 [l, r] 区间里面的每一个数都变为x: 如果是 2 l r x,则 比较 [l, r]区间里的数a_i和x的大小,如果a_i > x,把a_i变为a_i和x的最大公约数. 最后输出这n个数最终的值. 线段树可搞...但是没必要!... 线段树,每个结点多一个lazy表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候

求动区间第k小(离线,线段树)

#include<bits/stdc++.h> const int M = 1e5 + 10 ; int n , m ; int ql , qr , k ; int a[M] , sum [M] , p[M] ; int id = 0 ; void build (int o , int l , int r) { if (l == r) { sum[o] = 1 ; return ; } int mid = (l + r) >> 1 ; build (o << 1 , l