[ASDFZ-NOIP2016模拟]小X分砖块

小 X 分砖块
(brick.pas/c/cpp)

问题描述
小 X 喜欢跟着爸爸跑到建筑工地上去。
这天,小 X 看到一排砖,每块要么是白色的(0),要么是黑色的(1)。小 X 想把这排
砖分成若干非空段,使得每段白砖和黑砖块数的比例相同。
当然,小 X 可以直接把整排砖作为一段,那就太简单了。为了增加难度,小 X 想知道最
多能分成多少段,例如:
100011 = 10 + 0011(即样例 1,最多分成 2 段,比例为 1:1);
0001110000000001 = 0001 + 11000000 + 0001(即样例 2,最多分成 3 段,比例为 3:1) 。
小 X 百思不得其解,希望你帮帮他。

输入格式
第一行包含一个整数 N。我们将用 N 行来描述这排砖,初始时这排砖为空。
接下来 N 行,每行包含用一个空格隔开的两个整数 Ki , Ci Ci 只可能是 0 或 1),表示
在上一行描述完后尾部又有了 Ki 块颜色为 Ci 的砖。

输出格式
第一行包含一个整数,表示最多能分成的段数。

样例数据1
输入
3 1
1
3 0
2 1
输出
2
样例数据2
输入
4 3
0
3 1
9 0
1 1
输出
3

【数据范围】
对于 30%的数据,N=1。
对于 60%的数据,所有 Ki 均相等。
对于 100%的数据,1≤N≤100000,1≤ Ki ≤1000000000,砖的总块数不超过 1000000000。

分析:从给数据的方式和数据大小可以看出,我们只需读入所有数据存在数组里,然后for循环排查就可以了。考试中,我用了一个特别绕的方法,就是真的照着题目模拟,建三个临时数组(这一段的某颜色砖块总数、与最简整数比的余数、是最简整数比的几倍),每次算到前面的另一种颜色满足整除就判断这种颜色能不能满足,如果满足切一刀,两个数组又重置,反复如此……这样,逻辑的错误很容易出现,结果最后只得了前30%特判的送的分……其实,将指针前面部分看作一个整体,就很好做了,没有什么复杂的数组重置、逻辑判断,我好心疼我的分啊!QAQ

代码
因为我是一个蒟蒻,最讨厌有些人写博客不打注释了,我看着代码还要研究半天……所以,我的注释绝对打足了的:

//一个重要的提示:因为要把砖块切分成颜色比例相同的若干段,即每一段的比例必须等于总数之比 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;
const int maxn=100100;

int n,ans;
int a[maxn],b[maxn],s[maxn],t[maxn];

int getint()//读入优化 
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f; 
}

int main()
{
    freopen("brick.in","r",stdin);
    freopen("brick.out","w",stdout);

    n=getint();
    for(int i=1;i<=n;++i)
    {
        a[i]=getint();b[i]=getint();
        s[b[i]]+=a[i];
    }

    if(!s[0]||!s[1])//特判:只有一种颜色的砖,当然是一个一个分 
    {
        printf("%d\n",s[0]+s[1]);
        return 0;
    }

    for(int i=1;i<=n;++i)//相当于将指针之前都看作一个整体,若在指针处能将前面切成与总数之比相同的部分,而上次切的地方之前也满足总数之比,即指针处到前一次之间的砖块比例满足总数之比,那肯定在指针处可以ans++ 
    {
        int x=b[i],y=b[i]^1;
        if(LL(s[x])*t[y]%s[y]==0)//判断指针前另一种颜色是否满足比例(如:总数之比化简后黑:白=5:3,现在读到的是黑色,那前面所有的白色至少要是3的倍数,不然的话,黑色砖块要切出分数,肯定不满足比例) 
        {
            int z=LL(s[x])*t[y]/s[y]-t[x];//算一算还需要多少个才能和另一种颜色构成总数之比 
            if(z>=1&&z<=a[i])//现在这一段够加上这么多个(至于为什么要>=1,因为刚开始t数组都是0,也满足整除,所以z会出现负数,需舍去) 
                ++ans;
        }
        t[x]+=a[i];//更新本颜色在指针之前的总数 
    }

    printf("%d\n",ans);
    return 0;
}

终于,我也把我的超级难饶的代码调对了,竟然是因为倍数的精度问题,把倍数数组改成double型就可以了,代码如下:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

const int eps=1e-9;

int n,ans;
int tot[5],zj[5],cnt[5],ys[5];//zj是最简整数比,cnt是此段总数,ys是余数
double bs[5];//bs是倍数
struct node{
    int color,num;
}brick[100100];

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

int main()
{
    freopen("brick.in","r",stdin);
    freopen("brick.out","w",stdout);

    n=getint();
    if(n==1)//30%
    {
        ans=getint();
        printf("%d\n",ans);
        return 0;
    }

    for(int i=1;i<=n;++i)
    {
        brick[i].num=getint();
        brick[i].color=getint();
        tot[brick[i].color]+=brick[i].num;
    }

    int a=tot[0],b=tot[1],c;
    if(aif(b==0)
    {
        printf("%d\n",a);
        return 0;
    }
    while(a%b!=0)
    {
        c=a%b;
        a=b;
        b=c;
    }

    if(b==1)
    {
        printf("1\n");
        return 0;
    }
    zj[0]=tot[0]/b;
    zj[1]=tot[1]/b;
    cnt[brick[1].color]=brick[1].num;
    ys[brick[1].color]=cnt[brick[1].color]%zj[brick[1].color];
    bs[brick[1].color]=double(cnt[brick[1].color])/zj[brick[1].color];
    for(int i=2;i<=n;++i)
    {
        if(brick[i].color==0)
        {
            cnt[0]+=brick[i].num;
            bs[0]=double(cnt[0])/zj[0];
            ys[0]=cnt[0]%zj[0];
            if(ys[1]==0&&bs[1]>=1)
            {
                if(bs[0]-bs[1]>=-eps&&bs[0]-double(brick[i].num)/zj[0]+eps<=bs[1])
                {
                    cnt[0]=cnt[0]-int(bs[1])*zj[0];
                    cnt[1]=0;
                    ys[0]=cnt[0]%zj[0];
                    ys[1]=0;
                    bs[0]=double(cnt[0])/zj[0];
                    bs[1]=0;
                    ans++;
                }
            }
        }
        else
        {
            cnt[1]+=brick[i].num;
            bs[1]=double(cnt[1])/zj[1];
            ys[1]=cnt[1]%zj[1];
            if(ys[0]==0&&bs[0]>=1)
            {
                if(bs[1]-bs[0]>=-eps&&bs[1]-double(brick[i].num)/zj[1]+eps<=bs[0])
                {
                    cnt[1]=cnt[1]-int(bs[0])*zj[1];
                    cnt[0]=0;
                    ys[1]=cnt[1]%zj[1];
                    ys[0]=0;
                    bs[1]=double(cnt[1])/zj[1];
                    bs[0]=0;
                    ans++;
                }
            }
        }
    }

    printf("%d\n",ans);
    return 0;
}

本题结。

你可能感兴趣的:([ASDFZ-NOIP2016模拟]小X分砖块)