『树状数组·逆序对』飞fly

P r o b l e m \mathrm{Problem} Problem

liu_runda决定提高一下知识水平,于是他去请教郭神.郭神随手就给了liu_runda一道神题,liu_runda并不会做,于是把这个题扔到联考里给高二的做.

郭神有n条位于第一象限内的线段,给出每条线段与x轴和y轴交点的坐标,显然这样就可以唯一确定每一条线段.

n条线段和y轴交点的纵坐标分别为1,2,3,4…n.我们记和y轴交点纵坐标为i的线段和x轴交点的横坐标为x[i]+1,x[i]按这样的方式生成:

x[1]由输入给出.

x[i]=(x[i-1]+a) % mod,2<=i<=n.

即:如果x[3]=4,则与y轴交点纵坐标为3的抛物线,和x轴交点的横坐标为4+1=5.

我们保证给出的n,x[1],a,mod使得所有的x[i]互不相同.

对于第一象限内的所有点(点的横纵坐标可以是任意实数),如果一个点被x条线段经过,它的鬼畜值就是x*(x-1)/2.

求第一象限内的所有点的鬼畜值之和.

S o l u i t o n \mathrm{Soluiton} Soluiton

很容易将题目转化为逆序对问题,我们思考如何求解逆序对。

由于这里的n很大,我们无法做到每一步都求解逆序对,我们发现x是一个等差数列,那么一定会形成若干个递增的序列。

  • 若回到第一个个位置,直接统计逆序对。
  • 然后每一次都减去等差数列的个数,即如果原来形成了k个等差数列,那么每一次答案会减少k,脑补一下即可;

代码如下:

#include <cstdio>
#include <iostream>

#define int long long

using namespace std;

int n, x1, a, P, S[100000+10];

void add(int x) {
	for (int i=x;i<=1e5;i+=i&-i)
	    S[i] ++;
	return;
}

int ask(int x,int res = 0) {
	for (int i=x;i;i-=i&-i)
	    res += S[i];
	return res;
}

signed main(void)
{
	freopen("fly.in","r",stdin);
	freopen("fly.out","w",stdout);
	cin >> n >> x1 >> a >> P;
	int x = x1, k = 0, ans = 0, sum = 0; 
	add(x1 + 1);
	for (int i=2;i<=n;++i)
	{
		x = (x + a) % P;
		if (x < a) k ++, ans = i-1-ask(x+1), add(x+1);
		else if (x > x1) ans -= k;
		else ans = ans - k + 1;
		sum += ans;
	}
	cout<<sum<<endl;
	return 0;	
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值