强题!dft!

膜拜!dft!

题目来源

不知道是什么地方的集训题,总之很强就对了.

题目描述

这里写图片描述
这里写图片描述
这里写图片描述

题解

题目描述有点问题,y序列少写了个y0.
题意其实是这样的:
每次把[l,r]的序列取出来,进行dft变换,然后再放回原序列.
取出来进行变换是指,重新编号,并且变换只与[l,r]的序列有关,与原序列无关。

那么怎么做呢?先化简式子?理论上应该要化简式子,但是我的数学水平不足以解决这样的式子,那怎么办?先写个 O(n3) 的暴力看看.

于是我们可以发现一些规律:
对1,2,3,4进行两次变换之后变成了1,4,3,2;数值没有变,只有位置变化了;
对1,2,3,4,5,6进行两次变换变成了1,6,5,4,3,2.这下发现规律了!L位置的数不变,[L+1,R]的数反序;
那么询问的答案怎么算?根据打表结果,我们发现,这个变换一次之后的模长平方和等于变换之前的模长平方和。
这些结论我真的不会证明,也没有看到什么有用的题解,如果有大佬会证明的,可以在评论区教育我。

于是写个暴力就有50分了,如果用平衡树维护的话可以拿到100分。

Code

贴一份代码:

#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define MAXN 105000
int st[MAXN],top;
struct node
{
    int ch[MAXN][2],fa[MAXN],rev[MAXN],tot,root,size[MAXN];
    LL val[MAXN],sum[MAXN];
    void cal(int p)
    {
        sum[p]=val[p]+sum[ch[p][0]]+sum[ch[p][1]];
        size[p]=size[ch[p][0]]+size[ch[p][1]]+1;
        return ;
    }
    void zg(int x,int d)
    {
        int y=fa[x],z=fa[y];
        if(ch[z][0]==y)
        {
            ch[z][0]=x;
        }
        else
        {
            ch[z][1]=x;
        }
        fa[x]=z;
        ch[y][d]=ch[x][!d];
        fa[ch[y][d]]=y;
        ch[x][!d]=y;
        fa[y]=x;
        cal(y);
        cal(x);
        if(!fa[x])
        {
            root=x;
        }
        return ;
    }
    void Rev(int p)
    {
        swap(ch[p][0],ch[p][1]);
        rev[p]^=1;
        return ;
    }
    void push(int p)
    {
        if(rev[p])
        {
            if(ch[p][0])
            {
                Rev(ch[p][0]);
            }
            if(ch[p][1])
            {
                Rev(ch[p][1]);
            }
            rev[p]^=1;
        }
        return ;
    }
    void splay(int x,int to=0)
    {
        if(x==to)
        {
            return ;
        }
        int y,z;
        st[++top]=x;
        for(int i=x;fa[i]!=to;i=fa[i])
        {
            st[++top]=fa[i];
        }
        for(;top;--top)
        {
            push(st[top]);
        }
        while(fa[x]!=to)
        {
            y=fa[x];
            z=fa[y];
            if(z!=to)
            {
                if(ch[z][0]==y^ch[y][0]==x)
                {
                    zg(x,ch[fa[x]][1]==x);
                }
                else
                {
                    zg(y,ch[fa[y]][1]==y);
                }
            }
            zg(x,ch[fa[x]][1]==x);
        }
        return ;
    }
    void ins(LL v)
    {
        if(!root)
        {
            root=1;
            ++tot;
            sum[tot]=val[tot]=v;
            size[tot]=1;
            return ;
        }
        int p=root;
        while(ch[p][1])
        {
            p=ch[p][1];
        }
        size[++tot]=1;
        ch[p][1]=tot;
        sum[tot]=val[tot]=v;
        fa[tot]=p;
        splay(p);
        return ;
    }
    int kth(int k)
    {
        int p=root;
        while(p)
        {
            push(p);
            if(k==size[ch[p][0]]+1)
            {
                break;
            }
            else if(k<=size[ch[p][0]])
            {
                p=ch[p][0];
            }
            else if(k>size[ch[p][0]]+1)  //因为没写这两个else,我调了3个小时!简直闯了鬼了! 
            {
                k-=size[ch[p][0]]+1;
                p=ch[p][1];
            }
        }
        return p;
    }
    void print()
    {
        for(int i=2;i<tot;i++)
        {
            printf("%d ",val[kth(i)]);
        }
        putchar('\n');
        return ;
    }
}T;
int n,q,l,r;
void _r(int& x,bool f=0)
{
    char c=getchar();
    while(c<'0'||c>'9')
    {
        f|=(c=='-');
        c=getchar();
    }
    for(x=0;c>='0'&&c<='9';c=getchar())
    {
        x=(x<<1)+(x<<3)+c-'0';
    }
    x=f?-x:x;
    return ;
}
int o[30],oo;
void _w(LL x)
{
    if(x==0ll)
    {
        putchar('0');
        return ;
    }
    for(oo=0;x;x/=10ll)
    {
        o[++oo]=x%10ll;
    }
    for(;oo;--oo)
    {
        putchar('0'+o[oo]);
    }
    return ;
}
int main()
{
    //freopen("dft.in","r",stdin);
    //freopen("dft.out","w",stdout);
    _r(n);
    T.ins(0);
    for(int i=1,x;i<=n;i++)
    {
        _r(x);
        T.ins(1ll*x*x);
    }
    T.ins(0);
    _r(q);
    for(int i=1,a,b,c;i<=q;i++)
    {
        _r(l);
        _r(r);
        a=T.kth(l+1);
        T.splay(a);
        b=T.kth(r+2);
        T.splay(b,a);
        _w(T.sum[T.ch[b][0]]+T.val[a]);
        putchar('\n');
        T.Rev(T.ch[b][0]);
    }
    return 0;
}       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值