题目:

 

 

 

 分析:

看了一眼题,毕竟是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\n",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\n",ans);
}
/*
5 2 4 7
*/
正解100

 

posted on 2019-08-31 17:12  rua-rua-rua  阅读(182)  评论(0编辑  收藏  举报