[2017/5/28]FJ四校联考

来自FallDream的博客,未经允许,请勿转载,谢谢。


话说这一段时间算是过去了,好久好久之后终于又有联考了  没想到这次到我们学校出题,昨天才想起来,临时花一天赶了一套,我出了一个sbFFT,质量勉强吧。

upd:代码已经更新

A.种树

有一棵二叉树,有点权,你要从节点1出发前往一个节点,如果路上的节点权值和他相等就结束,否则比他小向左,比他大向右,问能不能找到。

需要支持多组询问,单点修改,子树反转

n,m<=10^5

#include<iostream>
#include<cstdio>
#define pa pair<int,int> 
#define mp(x,y) make_pair(x,y)
#define MN 100000
#define INF 2000000000 
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

struct data{int lmn,lmx,rmn,rmx;
    friend data operator +(data a,data b)
    {
        data c;
        c.lmn=min(a.lmn,b.lmn);
        c.rmx=max(a.rmx,b.rmx);
        c.lmx=(a.lmx==INF)?b.lmx:(b.lmx==INF?a.lmx:max(a.lmx,b.lmx)); 
        c.rmn=(a.rmn==-INF)?b.rmn:(b.rmn==-INF?a.rmn:min(a.rmn,b.rmn));
        return c;
    }
    void rev()
    {
        swap(lmn,rmn);if(lmn==-INF) lmn=INF;if(rmn==INF) rmn=-INF;
        swap(lmx,rmx);if(rmx==INF) rmx=-INF;if(lmx==-INF)lmx=INF;
    }
};
struct Tree{int l,r,rev;data x;}T[MN*4+5];
int l[MN+5],r[MN+5],num[MN+5],fa[MN+5],top[MN+5],n,m,w[MN+5],dn=0,nl[MN+5],nr[MN+5],size[MN+5];
pa p[MN+5];

void Dfs1(int x)
{
    if(l[x]) Dfs1(l[x]),fa[l[x]]=x;
    if(r[x]) Dfs1(r[x]),fa[r[x]]=x;
    size[x]=size[l[x]]+size[r[x]]+1; 
} 

void Dfs2(int x,int tp)
{
    if(!x) return;top[x]=tp;num[nl[x]=++dn]=x;
    if(size[l[x]]>size[r[x]]) Dfs2(l[x],tp),Dfs2(r[x],r[x]);
    else Dfs2(r[x],tp),Dfs2(l[x],l[x]);
    p[l[x]]=mp(1,w[x]);p[r[x]]=mp(2,w[x]);
    nr[x]=dn;
}

void build(int x,int l,int r)
{
    if((T[x].l=l)==(T[x].r=r))
    {
        int now=num[l];
        if(p[now].first==1)
            T[x].x=(data){p[now].second,p[now].second,-INF,-INF};
        else 
            T[x].x=(data){INF,INF,p[now].second,p[now].second};
        return;
    }
    int mid=l+r>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    T[x].x=T[x<<1].x+T[x<<1|1].x;
}

inline void Mark(int x)
{
    T[x].rev^=1;
    T[x].x.rev();    
}

inline void pushdown(int x)
{
    Mark(x<<1);Mark(x<<1|1);
    T[x].rev=0;
}

void Modify(int x,int v,pa k)
{
    if(T[x].l==T[x].r)
    {
        if(k.first==1^T[x].rev)
            T[x].x=(data){k.second,k.second,-INF,-INF};
        else 
            T[x].x=(data){INF,INF,k.second,k.second};
        return;
    }
    if(T[x].rev) pushdown(x);
    int mid=(T[x].l+T[x].r)>>1;
    if(v<=mid) Modify(x<<1,v,k);
    else if(v>mid) Modify(x<<1|1,v,k);
    T[x].x=T[x<<1].x+T[x<<1|1].x;
}

void Reverse(int x,int l,int r)
{
    if(T[x].l==l&&T[x].r==r){Mark(x);return;}
    int mid=(T[x].l+T[x].r)>>1;
    if(T[x].rev) pushdown(x);
    if(r<=mid) Reverse(x<<1,l,r);
    else if(l>mid) Reverse(x<<1|1,l,r);
    else Reverse(x<<1,l,mid),Reverse(x<<1|1,mid+1,r);
    T[x].x=T[x<<1].x+T[x<<1|1].x;
}

data Query(int x,int l,int r)
{
    if(T[x].l==l&&T[x].r==r) return T[x].x;
    if(T[x].rev) pushdown(x);
    int mid=(T[x].l+T[x].r)>>1;
    if(r<=mid) return Query(x<<1,l,r);
    else if(l>mid) return Query(x<<1|1,l,r);
    else return Query(x<<1,l,mid)+Query(x<<1|1,mid+1,r); 
}

data Solve(int x)
{
    data res=(data){INF,INF,-INF,-INF};
    for(;x;x=fa[top[x]]) res=res+Query(1,nl[top[x]],nl[x]);
    return res;
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i) w[i]=read(),l[i]=read(),r[i]=read();
    Dfs1(1);Dfs2(1,1);p[1]=mp(1,INF);build(1,1,n);
    for(int i=1;i<=m;++i)
    {
        int op=read(),x=read();
        if(op==1) 
        {
            int y=read();
            if(l[x]) Modify(1,nl[l[x]],mp(1,y));
            if(r[x]) Modify(1,nl[r[x]],mp(2,y));
            w[x]=y;    
        }
        if(op==2)
        {
            if(nl[x]!=nr[x])
                Reverse(1,nl[x]+1,nr[x]);    
        }
        if(op==3)
        {
            data y=Solve(x);
            if(w[x]<y.lmn&&w[x]>y.rmx) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}

把一条边看成一个限制条件,也就是大于或者小于某个值。那么树链剖分之后线段树维护就行了,维护区间小于限制的最大/最小值,大于限制的最大/最小值。

子树反转可以打标记实现。复杂度nlog^2n

代码貌似丢在学校了,端午过后补吧。

B.mark

C.秋之国的盛会

给定两个长度为n的序列ai,bi,可以把a向后推动0到n次。给定c,m,推动k次的时候的费用是$\sum{(\frac{ai}{bi}-c*k-m)^2}$

n<=10^5

求最小费用

把这个式子展开,很容易发现只要能快速求出不同推动状态下的$\sum{\frac{ai}{bi}}$和$\sum{(\frac{ai}{bi})^2}$即可。

把ai数组倒过来并复制一遍,b数组变成1/bi,fft求卷积即可。另一项同理。

复杂度nlogn

#include<iostream>
#include<cstdio>
#include<cmath>
#define pi acos(-1)
#define ld long double
#define MN 262144
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int N,n,M;double cost;
struct cp
{
    ld r,u;
    cp ( double x = 0,double y = 0) :r(x) , u(y) {}
    cp operator + (const cp&y) { return cp(r+y.r,u+y.u);}
    cp operator - (const cp&y) { return cp(r-y.r,u-y.u);}
    cp operator * (const cp&y) { return cp(r*y.r-u*y.u,r*y.u+u*y.r);}        
    cp operator * (ld y){return cp(r*y,u*y);}
    cp operator / (ld y){return cp(r/y,u/y);}
}A[MN+5],B[MN+5],C[MN+5],a[MN+5],b[MN+5],c[MN+5],w[2][MN+5];

void fft(cp*x,int r)
{
    for(int i=0,j=0;i<N;++i)
    {
        if(i>j) swap(x[i],x[j]);
        for(int l=N>>1;(j^=l)<l;l>>=1);    
    }
    for(int i=2;i<=N;i<<=1)for(int j=0;j<N;j+=i)for(int k=0;k<i>>1;++k)
    {
        cp t=x[j+k+(i>>1)]*w[r][N/i*k];
        x[j+k+(i>>1)]=x[j+k]-t;
        x[j+k]=x[j+k]+t;    
    }
    if(r)for(int i=0;i<N;++i) x[i]=x[i]/N;
}

int main()
{
    freopen("party.in","r",stdin);
    freopen("party.out","w",stdout); 
    n=read();M=read();scanf("%lf",&cost);
    for(int i=n-1;~i;--i)  a[i].r=read(),A[i].r=a[i].r*a[i].r;
    for(int i=0;i<n;++i)  b[i].r=(ld)1/read(),B[i].r=b[i].r*b[i].r;
    for(int i=0;i<n;++i) a[i+n]=a[i],A[i+n]=A[i]; 
    for(N=1;N<=n<<1;N<<=1);
    w[0][0]=w[1][N]=cp(1,0);w[0][1]=w[1][N-1]=cp(cos(2*pi/N),sin(2*pi/N));
    for(int i=2;i<=N;++i) w[0][i]=w[1][N-i]=w[0][i-1]*w[0][1];
    fft(a,0);fft(b,0);fft(A,0);fft(B,0);
    for(int i=0;i<N;++i) c[i]=a[i]*b[i],C[i]=A[i]*B[i];
    fft(c,1);fft(C,1);
    ld ans=1e99;int mx=0;
    for(int i=n-1;i<n<<1;++i)
    {
        ld j=cost*(i-n+1);
        if(C[i].r-2*c[i].r*(j+M)+(ld)n*(j+M)*(j+M)<ans) mx=i-n+2;
        ans=min(ans,C[i].r-2*c[i].r*(j+M)+(ld)n*(j+M)*(j+M)); 
    }
    printf("%.10lf",(double)ans);
    return 0;
}
posted @ 2017-05-28 23:42  FallDream  阅读(299)  评论(0编辑  收藏  举报