zoukankan      html  css  js  c++  java
  • [洛谷P5286] 一个简单的询问

    问题描述

    给你一个长度为 (N) 的序列 (a_i)(1leq ileq N),和 (q) 组询问,每组询问读入 (l_1,r_1,l_2,r_2),需输出

    (sumlimits_{x=0}^infty ext{get}(l_1,r_1,x) imes ext{get}(l_2,r_2,x))

    ( ext{get}(l,r,x)) 表示计算区间 ([l,r]) 中,数字 (x) 出现了多少次。

    输入格式

    第一行,一个数字 (N),表示序列长度。
    第二行,(N) 个数字,表示 (a_1sim a_N)
    第三行,一个数字 (Q),表示询问个数。
    (4sim Q+3) 行,每行四个数字 (l_1,r_1,l_2,r_2),表示询问。

    输出格式

    对于每组询问,输出一行一个数字,表示答案。

    样例输入

    5
    1 1 1 1 1
    2
    1 2 3 4
    1 1 4 4

    样例输出

    4
    1

    说明

    对于 (20\%) 的数据,(1leq N,Qleq 1000)
    对于另外 (30\%) 的数据,(1leq a_ileq 50)
    对于 (100\%) 的数据,(N,Qleq 50000)(1leq a_ileq N)(1leq l_1leq r_1leq N)(1leq l_2leq r_2leq N)

    数据范围与原题相同,但测试数据由 LibreOJ 自制,并非原数据。

    注意:答案有可能超过 int 的最大值。

    解析

    区间问题往往可以想到前缀和。如果想用前缀和的形式表示这个式子,那么可以按照如下过程化简:

    [egin{aligned} sum_{i=0}^{infty} get(l_1, r_1, x) imes get(l_2, r_2, x) = &sum_{i=0}^{infty} get(0, r_1, x) imes get(0, r_2, x)\ - &sum_{i=0}^{infty} get(0, l_1-1, x) imes get(0, r_2, x)\ - &sum_{i=0}^{infty} get(0, r_1, x) imes get(0, l_2-1, x) \ + &sum_{i=0}^{infty} get(0, l_1-1, x) imes get(0, l_2-1, x) end{aligned} ]

    这样,我们就把一个询问拆成了4个可以用莫队维护的询问。对于每一个询问,维护 (num[0][x]) 表示在区间 ([1,l])(x) 出现了多少次,(num[1][x]) 表示区间 ([1,r])(x) 出现了多少次。那么答案就是 (sumlimits_{x=0}^infty num[0][x]*num[1][x]) 。至于修改操作,可以这么看:假设一个数原来是 (a*b) ,现在要把它变成 ((a+1)*b) ,其实就相当于在原来的基础上加上一个 (b) 。这里也是同理。

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #define int long long
    #define N 50002
    using namespace std;
    struct query{
    	int l,r,id;
    }q[4*N];
    int n,m,i,a[N],b[N],gap,cnt,num[2][N],sum,ans[4*N],l,r;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    int my_comp(const query &x,const query &y)
    {
    	if(b[x.l]==b[y.l]) return x.r<y.r;
    	return x.l<y.l;
    }
    void add(int op,int x)
    {
    	num[op][a[x]]++;
    	sum+=num[op^1][a[x]];
    }
    void del(int op,int x)
    {
    	num[op][a[x]]--;
    	sum-=num[op^1][a[x]];
    }
    signed main()
    {
    	n=read();
    	gap=sqrt(1.0*n);
    	for(i=1;i<=n;i++) a[i]=read();
    	for(i=1;i<=n;i++) b[i]=(i-1)/gap+1;
    	m=read();
    	for(i=1;i<=m;i++){
    		int l1=read(),r1=read(),l2=read(),r2=read();
    		q[++cnt]=(query){min(r1,r2),max(r1,r2),cnt};
    		q[++cnt]=(query){min(r1,l2-1),max(r1,l2-1),cnt};
    		q[++cnt]=(query){min(l1-1,r2),max(l1-1,r2),cnt};
    		q[++cnt]=(query){min(l1-1,l2-1),max(l1-1,l2-1),cnt};
    	}
    	sort(q+1,q+cnt+1,my_comp);
    	for(i=1;i<=cnt;i++){
    		while(l<q[i].l) add(0,++l);
    		while(l>q[i].l) del(0,l--);
    		while(r<q[i].r) add(1,++r);
    		while(r>q[i].r) del(1,r--);
    		ans[q[i].id]=sum;
    	}
    	for(i=1;i<=cnt;i+=4) printf("%lld
    ",ans[i]-ans[i+1]-ans[i+2]+ans[i+3]);
    	return 0;
    }
    
  • 相关阅读:
    java微信扫码支付(模式二)
    Python学习08
    学习java第12天
    学习java第11天
    学习java第十天
    学习Java第九天
    学习Java第八天
    学习Java第六天
    学习Java第六天
    学习Java第五天
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12194881.html
Copyright © 2011-2022 走看看