JZOJ 5422. 【NOIP2017提高A组集训10.25】天才绅士少女助手克里斯蒂娜

Description

Description

Input

第一行两个整数n;m 表示电子个数和询问个数.
接下来n 行, 每行两个整数x; y 表示vi.
接下来m 行, 每行形如1 p x y 或2 l r, 分别表示两种操作.

Output

对于每个操作2, 输出一行一个整数表示飘升系数对20170927 取模的值.

Sample Input

9 5
13052925 5757314
9968857 11135327
13860145 3869873
6912189 3461377
2911603 7061332
6334922 7708411
5505379 5915686
6806727 588727
7603043 15687404
2 1 6
1 7 2602783 18398476
1 8 8636316 19923037
2 2 7
2 2 4

Sample Output

18529202
963126
19167545

Data Constraint

Data Constraint

Solution

  • 首先,我们要知道向量的叉积,如何计算:

  • 对于两个平面向量 (x1,y1) (x2,y2) ,根据定义,其叉积即为: x1y2x2y1

  • 而题目需要求:

    li<jr|vivj|2=li<jr(aibjajbi)2

  • 经过加减的转化,上式可以化为:

    (i=1na2i)(i=1nb2i)(i=1naibi)2

  • 这样每个值都只与自己有关,于是就可以用线段树维护。

  • 每个点存储 a2i b2i aibi 这三种值,再区间分别维护这三种值的和即可。

  • 当修改单个向量时,就在线段树中单点修改即可。

  • 时间复杂度为 O((N+M) log N)

Code

#include<cstdio>
using namespace std;
const int N=1e6+1,mo=20170927;
struct data{long long x,y,z;}a[N],z;
data operator +(data x,data y)
{
    z.x=(x.x+y.x)%mo;
    z.y=(x.y+y.y)%mo;
    z.z=(x.z+y.z)%mo;
    return z;
}
struct Stream
{
    inline int read()
    {
        int X=0,w=1; char ch=0;
        while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
        return X*w;
    }
    inline void write(int x)
    {
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
}Std;
struct Segment_Tree
{
    data f[N<<2];
    inline void make(int v,int l,int r)
    {
        if(l==r)
        {
            f[v].x=a[l].x*a[l].x%mo;
            f[v].y=a[l].y*a[l].y%mo;
            f[v].z=a[l].x*a[l].y%mo;
            return;
        }
        int mid=(l+r)>>1;
        make(v<<1,l,mid);
        make(v<<1|1,mid+1,r);
        f[v]=f[v<<1]+f[v<<1|1];
    }
    inline void change(int v,int l,int r,int x,long long y,long long z)
    {
        if(l==r)
        {
            f[v].x=y*y%mo;
            f[v].y=z*z%mo;
            f[v].z=y*z%mo;
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid) change(v<<1,l,mid,x,y,z); else change(v<<1|1,mid+1,r,x,y,z);
        f[v]=f[v<<1]+f[v<<1|1];
    }
    inline data find(int v,int l,int r,int x,int y)
    {
        if(l>=x && r<=y) return f[v];
        int mid=(l+r)>>1;
        if(y<=mid) return find(v<<1,l,mid,x,y);
        if(x>mid) return find(v<<1|1,mid+1,r,x,y);
        return find(v<<1,l,mid,x,mid)+find(v<<1|1,mid+1,r,mid+1,y);
    }
}Tree;
int main()
{
    int n=Std.read(),m=Std.read();
    for(int i=1;i<=n;i++) a[i].x=Std.read(),a[i].y=Std.read();
    Tree.make(1,1,n);
    while(m--)
    {
        int type=Std.read(),l=Std.read();
        if(type==1)
        {
            long long x=Std.read(),y=Std.read();
            Tree.change(1,1,n,l,x,y);
        }else
        {
            data t=Tree.find(1,1,n,l,Std.read());
            Std.write((t.x*t.y%mo+mo-t.z*t.z%mo)%mo);
            putchar('\n');
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值