题目:
分析:
看了一眼题,毕竟是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 */
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 */