zoukankan      html  css  js  c++  java
  • test20180922 古代龙人的谜题

    题意

    问题描述

    Mark Douglas是一名调查员。他接受了「调查古代龙人」的任务。经过千辛万苦,Mark终于找到了一位古代龙人。Mark找到他时,他正在摆弄一些秘药,其中一些药丸由于是从很久以前流传下来的,发出了独特的光泽。古代龙人告诉了Mark一些他想知道的事情,看了看手中的秘药,决定考一考这位来访者。
    古代龙人手中共有n粒秘药,我们可以用1表示「古老的秘药」,其余的用0表示。他将它们排成一列。古代龙人认为平衡是美的,于是他问Mark能选出多少个「平衡的区间」。「平衡的区间」是指首先选出一个区间[L, R],在它内部选出一个中间点mid,满足L<mid<R,mid是「古老的秘药」,且区间[L, mid]和[mid, R]中「古老的秘药」个数相等。

    输入格式

    输入文件名为puzzle.in。
    第一行为一个正整数idx表示该测试点所属的子任务编号,子任务的详细信息请见「数据范围」。样例的子任务编号为0。
    第二行为一个正整数n。
    第三行为一个长度为n的字符串,仅包含0和1。

    输出格式

    输出文件名为puzzle.out。
    输出仅一行表示答案。

    样例输入

    0
    7
    1101011

    样例输出

    7

    数据范围

    本题采用捆绑测试,只有通过一个子任务中的全部测试点才能拿到这个子任务的分数。
    对于所有子任务:1≤n≤〖10〗^6。各子任务分值及特殊约束如下:
    子任务1(8%):1≤n≤50。
    子任务2(26%):1≤n≤300。
    子任务3(24%):1≤n≤2000。
    子任务4(5%):n≥3,字符串中仅有3个1。
    子任务5(12%):字符串中全是1。
    子任务6(25%)。

    分析

    考场做法

    考虑分别计算以i为结尾,起始在[1,i-1]内的合法区间个数。
    按串的0/1分为两种情况。

    1. 此位是0。先算前面0的贡献,考虑维护一个cnt[2]数组,令每遇到一个1就让cur异或1,统计0的个数,相当于按1划分0的段,显然此位的答案应加上cnt[cur^1]。然后算1的贡献,应该是此位之前1的中,距它中间间隔了正偶数个1的1的个数。
    2. 此位是1。先算前面0的贡献,应该是与此1同段的,且是非相邻0段的0的个数。所以要维护一个last数组,代表此位之前最近的1的位置。然后算1的贡献,应该是此位之前的1中,距它中间间隔了正奇数个1的1的个数。

    代码

    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<iostream>
    #include<string>
    #include<vector>
    #include<list>
    #include<deque>
    #include<stack>
    #include<queue>
    #include<map>
    #include<set>
    #include<bitset>
    #include<algorithm>
    #include<complex>
    #pragma GCC optimize ("O0")
    using namespace std;
    template<class T> inline T read(T&x)
    {
        T data=0;
    	int w=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
    		if(ch=='-')
    			w=-1;
    		ch=getchar();
    	}
        while(isdigit(ch))
            data=10*data+ch-'0',ch=getchar();
        return x=data*w;
    }
    typedef long long ll;
    const int INF=0x7fffffff;
    
    const int MAXN=1e6+7;
    
    int n;
    char s[MAXN];
    
    int last[MAXN];
    int pre[MAXN];
    int cnt[2],cur=0;
    int f[MAXN];
    /*
    0
    5
    10010
    */
    int main()
    {
      freopen("puzzle.in","r",stdin);
      freopen("puzzle.out","w",stdout);
    	int id;
    	read(id);
    	read(n);
    	scanf("%s",s+1);
    	ll ans=0;
    	for(int i=1;i<=n;++i)
    	{
    		pre[i]=pre[i-1]+(s[i]=='1');
    		if(s[i]=='0')
    		{
    			f[i]=cnt[cur^1]+(pre[i]-1)/2;
    			++cnt[cur];
    			last[i+1]=last[i];
    		}
    		else if(s[i]=='1')
    		{
    			f[i]=pre[i-1]/2+cnt[cur]-(i-last[i]-1);
    //			cerr<<" cnt="<<cnt[cur]<<" judge="<<judge(i)<<endl;
    			cur^=1;
    			last[i+1]=i;
    		}
    //		cerr<<i<<" f="<<f[i]<<endl;
    //		cerr<<" cur="<<cur<<" cnt="<<cnt[cur]<<endl;
    		ans+=f[i];
    	}
    	printf("%lld
    ",ans);
    //  fclose(stdin);
    //  fclose(stdout);
        return 0;
    }
    

    题解

    由于当我们确定了区间的两个端点以后,合法的中间点最多只有一个,且只要中间点存在(即区间[L, R]内有奇数个1)该区间就合法,所以我们枚举左端点L,同时维护在L右边且区间[1, R]内1的个数为奇数/偶数的点R的个数,答案就可以统计出来了。

    std

    #include <cstdio>
    
    using LL = long long;
    
    const int MAXN = 1e6 + 5;
    
    int n, seq[MAXN];
    int cnt, right[MAXN];
    LL sum[2];
    
    int main() {
    #ifndef LOCAL
        freopen("puzzle.in", "r", stdin);
        freopen("puzzle.out", "w", stdout);
    #endif
        
        scanf("%*d%d", &n); cnt = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%1d", seq + i);
            cnt += seq[i];
        }
        
        int tmp = 0;
        for (int i = n, j = cnt; i; --i) {
            ++tmp;
            if (seq[i] == 1) {
                right[j--] = tmp; tmp = 0;
            }
        }
        sum[0] = sum[1] = 0;
        for (int i = 1; i <= cnt; i += 2) sum[1] += right[i];
        for (int i = 2; i <= cnt; i += 2) sum[0] += right[i];
        
        LL ans = 0;
        
        tmp = 0;
        for (int i = 1, j = 1; i <= n; ++i) {
            ++tmp;
            if (seq[i] == 1) {
                ans += (tmp - 1ll) * (right[j] - 1ll) + tmp * (sum[j & 1] - right[j]);
                sum[j & 1] -= right[j];
                ++j; tmp = 0;
            }
        }
        
        printf("%lld
    ", ans);
        
        return 0;
    }
    
  • 相关阅读:
    123
    p1216
    离线可持久化动态树
    线段树合并
    p2024
    树的dfs序,p1539,p1651,,2018/11/08模拟赛T3
    p1460
    CDQ分治,二维数点与三维数点,p1357与p2026与p2027与p2028与p2029
    自动AC机
    平衡二叉树之splay p1998
  • 原文地址:https://www.cnblogs.com/autoint/p/9691316.html
Copyright © 2011-2022 走看看