zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:斯诺(snow)(数学+前缀和+树状数组)

    题目传送门(内部题37)


    输入格式

    第一行一个整数$n$,表示区间的长度.
    第二行一个长度为$n$的只包含$0,1,2$的字符串,表示给出的序列。


    输出格式

    一行一个整数,表示革命的区间的数量。


    样例

    样例输入:

    10
    0000111011

    样例输出:

    8


    数据范围与提示

    第$1$个测试点,$n=100$
    第$2,3$个测试点,$n=1,000$
    第$4$个测试点,$n=50,000$
    第$5,6$个测试点,$n={10}^5$
    第$7,8,9,10$个测试点,$n=5 imes {10}^6$
    第$2,4,5,7$个测试点还满足:给出的序列中只含$0$和$1$


    题解

    显然需要用到前缀和,那么我们设$s_0,s_1,s_2$分别表示$0,1,2$的前缀和。

    那么对于一个区间$(l,r)$,只有当这个区间满足$s_r-s_{l-1}>frac{r-l+1}{2}$的时候满足,化简式子,$2 imes s_r+r-1>2 imes s_{l-1}+l$。

    但是显然这样求会重复,那么我么考虑这样一个问题,对于一个区间,$0,1,2$中只有一个可能会超过区间的一半,所以我们可以找出不满足的个数,用总的减去即可。

    那么我们所要求的就是当前位置以前有几个点满足这个性质,可以用树状数组快速求出,维护三个树状数组即可。

    我的做法稍特殊,首先在每个树状数组开始插一个$0$,然后我们只要求出小于等于$s_i-i$的个数即可。

    时间复杂度:$Theta(nlog n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    char ch[5000001];
    int a[5000001];
    int s[3][5000001];
    int tr0[20000010],tr1[20000010],tr2[20000010];
    long long ans;
    int lowbit(int x){return x&-x;}
    void add0(int x){for(int i=x;i<=10000000;i+=lowbit(i))tr0[i]++;}
    int ask0(int x)
    {
    	int res=0;
    	for(int i=x;i;i-=lowbit(i))res+=tr0[i];
    	return res;
    }
    void add1(int x){for(int i=x;i<=10000000;i+=lowbit(i))tr1[i]++;}
    int ask1(int x)
    {
    	int res=0;
    	for(int i=x;i;i-=lowbit(i))res+=tr1[i];
    	return res;
    }
    void add2(int x){for(int i=x;i<=10000000;i+=lowbit(i))tr2[i]++;}
    int ask2(int x)
    {
    	int res=0;
    	for(int i=x;i;i-=lowbit(i))res+=tr2[i];
    	return res;
    }
    int main()
    {
    	scanf("%d%s",&n,ch+1);
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=ch[i]-'0';
    		s[a[i]][i]++;
    		s[0][i]+=s[0][i-1];
    		s[1][i]+=s[1][i-1];
    		s[2][i]+=s[2][i-1];
    	}
    	add0(5000001);
    	add1(5000001);
    	add2(5000001);
    	for(int i=1;i<=n;i++)
    	{
    		ans+=i;
    		ans-=ask0(2*s[0][i]-i+5000000);
    		ans-=ask1(2*s[1][i]-i+5000000);
    		ans-=ask2(2*s[2][i]-i+5000000);
    		add0(2*s[0][i]-i+5000001);
    		add1(2*s[1][i]-i+5000001);
    		add2(2*s[2][i]-i+5000001);
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    POJ3928 Pingpong(统计比 K 小的个数 + 树状数组)
    C++ Primer Plus读书笔记
    HDU1698Just a Hook(线段树 + 区间修改 + 求和)
    POJ3468A Simple Problem with Integers(区间加数求和 + 线段树)
    POJ2528Mayor's posters(离散化 + 线段树)
    约瑟夫环
    编写一个JavaWeb项目
    四则运算在线答题系统
    JAVA项目中的常用的异常处理情况
    第八周动手动脑
  • 原文地址:https://www.cnblogs.com/wzc521/p/11496485.html
Copyright © 2011-2022 走看看