您的位置:首页 > 其它

HDU-4407 Sum(容斥定理+伪离线处理)

2017-12-03 20:33 218 查看
  前述:这周末还是一如既往的补作业加刷题,感觉自己真的得了嗜睡症,一到周末没人叫就绝对起不来,真是浪费青春的大好时光啊

周末做的这几个题里面,大部分是容斥定理的题目,但是就容斥定理本身来讲,这个题目才算是我真正入门的题目,为什么怎么说呢,这个专题时间挺短的,但是有的题目确实简单的很,在进入这个专题之前我一共看了三个相关的博客,一个是组合数的,两个是容斥定理的,然后基本仿照着这三个博客的说法(他的容斥定理的写法我并没有看懂)a了10道题,基本没费什么脑子,就是拐点小弯,还都是1a的,所以也一直担心被赶超,可是就同期一起做专题的人的速度看,好像他们并没有那么轻松,估计是学了题解上的那个写法,还没看懂,没法做到融会贯通。这也是我应该吸取的教训(因为大部分的博客上面都是位运算的写法,但是我并没有学,而是根据定义写了一发,一直到现在都没出现tle的情况就一直沿用了,一直到做完GCD这道题,刨根问底之后,两个写法我都算是掌握了,而且神奇的发现,其实二者在时间上没有快慢之分,只不过位运算的写法看比较简单,但相对难理解就是了)。

  题目:HDU-4407 Sum

  题目大意:就是给两种操作,第一种就是求x-y中和p互素的数的和,第二种就是改变某个数的值。

  解题思路:其实对于我这种刚刚入门的菜鸟,这种题目还是让我煞费一番心思的,因为操作的次数很少,想到了用离线处理,然后就是容斥定理求和,在这个题目开始我才把容斥定理这个思想彻底推广到数集上,达到真正的举一反三,所以说这个题目是我的入门题目。这里面离线操作的时候遇到了一个大问题,就是万一两次都操作一个数怎么办?顿时有点方,想了好几种方法,最终都发现行不通,时间复杂度不允许,因为每次进行离线操作的时候都需要memset处理。于是想到了用一个数组存他最后变成了什么,如果和变成的那个数一样就对这一次进行操作。那么为什么是伪离线操作呢,这里只用到了思想,却不完全一样,离线操作是输入的时候记录过程,输入结束后在进行操作,而我这里选择了记录过程+在线的操作。

  AC代码:

#include<iostream>

#include<cmath>

#include<cstring>

#include<cstdio>

#include<algorithm>

using namespace std;

long long fac[1100];

long long box[1100];

long long ch1[2010];

long long visit[400010];

long long ch2[2010];

long long n,m,t,k,total;

long long fun;

long long gcd(long long a,long long b){

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

}

void getfac(long long p){

    long long i,j;

    long long xx=p;

    total=0;

    for (i=2;i<=sqrt(p);i++){

        if (xx%i==0){

            fac[++total]=i;

            while (xx%i==0){

                xx/=i;

            }

        }

    }

    if (xx>1)fac[++total]=xx;

}

void sfind(long long x,long long y,long long z,long long val){

    long long i,j;

    for (i=x;i<=total;i++){

        box[y]=fac[i];

        if (y==z){

            long long temp=1;

            for (j=1;j<=z;j++){

                temp=temp/gcd(temp,box[j])*box[j];

            }

            long long xx=val/temp;

            fun+=temp*xx+temp*xx*(xx-1)/2;

        }

        else sfind(i+1,y+1,z,val);

    }

}

long long solve(long long x){

    long long i,j;

    long long temp=1;

    long long ans=0;

    for (i=1;i<=total;i++){

        fun=0;

        sfind(1,1,i,x);

        ans+=fun*temp;

        temp=-temp;

    }

    ans=x+x*(x-1)/2-ans;

    return ans;

}

int main(){

    long long i,j,l,r,x,y,z,zz;

    scanf("%lld",&t);

    while (t--){

        scanf("%lld%lld",&n,&m);

        memset (ch1,0,sizeof(ch1));

        for (i=1;i<=n;i++)visit[i]=i;

        for (i=1;i<=m;i++){

            scanf("%lld%lld%lld",&x,&y,&z);

            if (x==2){

                ch1[i]=y;

                cout<<y<<endl;

                ch2[i]=z;

                visit[y]=z;

            }

            else {

                scanf("%lld",&zz);

                getfac(zz);

                long long ans=solve(z)-solve(y-1);

                for (j=1;j<=i-1;j++){

                    if (ch1[j]==0)continue;

                    if (visit[ch1[j]]!=ch2[j])continue;

                    if (ch1[j]<y||ch1[j]>z)continue;

                    long long temp1=gcd(ch2[j],zz);

                    long long temp2=gcd(ch1[j],zz);

                    if (temp1<0)temp1=-temp1;

                    if (temp2<0)temp2=-temp2;

                    if (temp1==1&&temp2!=1){

                        ans+=ch2[j];

                    }

                    else if (temp1==1&&temp2==1){

                        ans=ans-ch1[j]+ch2[j];

                    }

                    else if (temp1!=1&&temp2==1){

                        ans=ans-ch1[j];

                    }

                }

                printf("%lld\n",ans);

            }

        }

    }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: