• 暑假考试题9:fly 飞(树状数组 逆序对)


    题目:

     

     分析:

    看了一眼题,毕竟是T3嘛,打个暴力。。。计算两两线段的交点,按照题意统计答案。

    复杂度n^2,得分20分

    #include<bits/stdc++.h>
    using namespace std;
    #define N 105
    #define eps 1e-6
    double X[N*N],Y[N*N],sum[N*N];
    int x[N];
    int read()
    {
        int x=0; int fl=1; char ch=getchar();
        while(ch>'9'||ch<'0') { if(fl=='-') fl=-1; ch=getchar(); }
        while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
        return x*fl;
    }
    int main()
    {
        freopen("fly.in","r",stdin);
        freopen("fly.out","w",stdout);
        int n,a,mod;    
        n=read(); x[1]=read(); a=read(); mod=read();
        for(int i=2;i<=n;i++) x[i]=(x[i-1]+a) %mod ;
        for(int i=1;i<=n;i++) x[i]++;
        int ans=0,cnt=0;
        for(int i=1;i<=n;i++)
         for(int j=i+1;j<=n;j++){
             double k1=(double) -i*1.0/x[i] ,k2=(double) -j*1.0/x[j];
             double xx=(double) (i-j)*1.0/(k2-k1) ,yy=xx*k1+i*1.0;
             if(xx<0||yy<0) continue;
             bool fl=false;
             for(int k=1;k<=cnt;k++)
             if( fabs(xx-X[k])<eps && fabs(yy-Y[k])<eps ) ans+=sum[k],sum[k]++,fl=true;
             if(!fl) X[++cnt]=xx,Y[cnt]=yy,sum[cnt]+=2,ans++;
        }
        printf("%d
    ",ans);
    }
    /*
    5 2 4 7
    */
    暴力20

    40分:

    但是其实仔细分析一下,会发现很多蹊跷的地方:y的坐标是单调递增的1,2,3,……

    把y轴倒过来看,是不是很像数组的下标呢。。。两两线段之间有交点的条件是:下标大的x值更小。

    这对应着什么?逆序对!!

    所以说对于每一个数,记录一下前面比它小的数有多少个,用树状数组维护一下,就可以得40分了。

    100分:

    剩下的卡空间告诉我们,x是不能直接递推求出来的,必须再找一下题中透露的性质。

    然后我们发现了这个:保证给出的数据使得x互不相同。

    在不%mod之前,数列成单调递增的等差数列,公差=a。%mod之后,变成一段一段的等差数列。(可以看出许多组)

    为了不出现重复的x,组数不超过a,而a又不大,可以利用a来维护树状数组。

    利用单调递增的性质,维护每一组的段头,给同一组中的数一个排号。

    对于任意一个数,它前面比它小的数是排名-1) * 组数  +  比段头小的数的个数(在排名相同的情况下 只要是比这个段头小的 都比这个小

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000005
    #define ll long long
    int shu[N],a;
    int read()
    {
        int x=0; int fl=1; char ch=getchar();
        while(ch>'9'||ch<'0') { if(fl=='-') fl=-1; ch=getchar(); }
        while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
        return x*fl;
    }
    int lowbit(int x){return x&(-x);}
    int query(int x){int s=0;while(x>0){s+=shu[x];x-=lowbit(x);}return s;}
    void add(int x){while(x<=a)shu[x]++,x+=lowbit(x);}
    int main()
    {
        freopen("fly.in","r",stdin);
        freopen("fly.out","w",stdout);
        ll ans=0;
        int n,x,mod,sum=0,cnt=1,num=0;
        n=read(); x=read(); a=read(); mod=read();
        int st=x,i;
        for(i=2;i<=n;i++){
            x+=a;
            if(x>=mod) break;//先将小于mod的一段跳过 
        }
        for(;i<=n;i++){
            if(x>=mod){//如果>=mod 则是一个段的开头 
                x-=mod; cnt=1; sum=query(x+1); add(x+1); num++;
                //cnt指的是目前这个段里面的排名 sum求的是前面每个段头比它小的个数 num指有多少个段  
            }
            int res= x>st ? (x-st)/a+1 : 0;//res处理的是前面未加入树状数组的单增等差序列 (x-st)/a+1 求在那一段中小于它的数的个数 
            int her=num*(cnt-1)+res+sum;//排名-1 * 段数 求出了比它小的数 
            //而+sum是因为在排名相同的情况下 只要是比这个段头小的 都比这个小 所以+sum 
            ans+=i-1-her;//求的是逆序对 -1不包括它自己 
            x+=a; cnt++;//排名++ 
        }
        printf("%lld
    ",ans);
    }
    /*
    5 2 4 7
    */
    正解100
  • 相关阅读:
    JS函数强化
    Javascript创建对象的方式
    call和apply的区别
    事件绑定和普通事件有什么区别
    又走一个
    风的季节
    关于Dictionary的线程安全问题
    进程管理简述
    开通
    WPF 音乐播放器界面
  • 原文地址:https://www.cnblogs.com/mowanying/p/11439716.html
Copyright © 2020-2023  润新知