zoukankan      html  css  js  c++  java
  • 20190927

    前言

    其实我也想直接上题解,但这会让不想颓的人无意间颓到个标签之类的尴尬事件发生。

    所以,我的每篇题解还是会说些废话的……

    这次不想说太多,只想提一句:

    所有题全部#define int long long!!!

    内存允许的话能开long long就开long long!!!

    一定考虑要不要开long long!!!

    T1

    正解是差分。

    然而我打了一颗线段树(事实上我只是拿线段树将差分$Theta(1)$标记的过程生硬的改成$Theta(logN)$的区间加……)。

    我们建n棵线段树。

    假设要修改(r,c),长度为l,增加的权值是s。

    我们先将第r行所对应的线段树的[r,c]都加上s,然后在c-1的位置累加上-s的标记1,c的位置累加上s的标记2。

    标记1和2是不同的标记,标记1下传到下一行的同一位置,标记2下传到下一行的下一位置,即min(c+1,n)。

    每次下传标记时进行一次1到目标位置的区间加法,例如第x行第y个位置下传标记1时需要进行一次第x+1行[1,y]的区间加。

    标记1最初赋值为-s,所以是区间加不是区间减。

    因为长度只有l,所以需要对第r+l-1行c-1的位置的标记1累加s,第r+l-1行min(c+l-1,n)的位置的标记2累加-s进行抵消。

    最后遍历每一行所对应的线段树,将区间加的懒标记全部下传以确保单点权值的正确。

    遍历到单点时权值与ans进行异或操作即可。

    注意打标记是$Theta(1)$的,因为相同纵坐标的位置在每棵线段树的位置(即数组下标)相同。

    总时空复杂度$Theta(N^2logN)$。

    然而我按这个思路交上去的代码爆零了……

    这……q=0我都没过,why?

     !!!

     

     ……

    然而还是WA。

    我百思不得其解,在调了半个小时后听从Joe神的劝告#define int long long,结果:

     ???

    在我认真调试后发现我的线段树add函数传参中增量的值开成了int,然而它要开longlong(因为下传标记时进行区间加的增量是longlong)。

    #include<cstdio>
    #define ll long long
    #define L k<<1
    #define R k<<1|1
    using namespace std;
    int const N=1003;
    inline int read(){
        int ss=0;char bb=getchar();
        while(bb<48||bb>57)bb=getchar();
        while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
        return ss;
    }
    int n,q,id[N];
    ll ans;
    inline int min(int x,int y){
        return x<y?x:y;
    }
    struct S_tree{
        struct node{
            ll w,f,mf,cf;
        }a[N<<2];
        inline void down(int k,int x,int y){
            if(!a[k].f || x==y)return ;
            ll z=a[k].f;
            int mid=x+y>>1;
            a[k].f=0;
            a[L].w+=z*(mid-x+1),a[L].f+=z;
            a[R].w+=z*(y-mid),a[R].f+=z;
            return ;
        }
        inline void update(int k){
            a[k].w=a[L].w+a[R].w;
            return ;
        }
        void add(int l,int r,int x,int y,ll p,int k){
            if(l>=x && r<=y){a[k].w+=p*(r-l+1),a[k].f+=p;return ;}
            down(k,l,r);
            int mid=l+r>>1;
            if(x<=mid)add(l,mid,x,y,p,L);
            if(y>mid)add(mid+1,r,x,y,p,R);
            return update(k);
        }
        void qj(int l,int r,int k){
            if(l==r){ans^=a[k].w;return ;}
            down(k,l,r);
            int mid=l+r>>1;
            qj(l,mid,L),qj(mid+1,r,R);
            return ;
        }
    }tr[N];
    void dfs(int now,int x,int y,int k){
        if(x==y){
            ll z;
            if(tr[now].a[k].mf){
                z=tr[now].a[k].mf;
                tr[now+1].add(1,n,1,min(x+1,n),z,1);
                tr[now+1].a[id[min(x+1,n)]].mf+=z;
            }
            if(tr[now].a[k].cf){
                z=tr[now].a[k].cf;
                tr[now+1].add(1,n,1,x,z,1);
                tr[now+1].a[id[x]].cf+=z;
            }
            ans^=tr[now].a[k].w;
            return ;
        }
        tr[now].down(k,x,y);
        int mid=x+y>>1;
        dfs(now,x,mid,L),dfs(now,mid+1,y,R);
        return ;
    }
    void get(int x,int y,int k){
        if(x==y){id[x]=k;return ;}
        int mid=x+y>>1;
        get(x,mid,L),get(mid+1,y,R);
        return ;
    }
    signed main(){
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        n=read(),q=read();
        if(!q)return puts("0"),0;
        get(1,n,1);
        while(q--){
            int r=read(),c=read(),l=read();
            ll s=read();
            tr[r].add(1,n,c,c,s,1);
            tr[r].a[id[c]].mf+=s;
            if(c^1){
                tr[r].a[id[c-1]].cf-=s;
                if(r+l-1<=n)tr[r+l-1].a[id[c-1]].cf+=s;
            }
            if(r+l-1<=n)tr[r+l-1].a[id[min(c+l-1,n)]].mf-=s;
        }
        for(register int i=1;i<n;++i)
            dfs(i,1,n,1);
        tr[n].qj(1,n,1);
        printf("%lld",ans);
        return 0;
    }
    惨!

    T2

    相当于记忆化搜索。

    记录状态(st,x)下的搜索答案,再次搜到直接回溯。

    其中st是剩余球的状态,x是进行到第几次操作。

    注意位运算的运用可大大缩短运行时间。

    还有搜索第x次操作时对于长度为n-x+1的可行域我们只搜一半即可,另一半是一样的。

    对(st,x)的状态记录需要用Hash表。

    一般都记录了点对,但因为x值域只有[1,30],所以我直接开了30个Hash表,插入和查询状态(st,x)时直接在第x个Hash表中操作。

    复杂度并不会证明,据说状态上界为$sumlimits_{i=1}^{n+1}Fib(i)$。

    upd.大神skyh的证明:

    我的代码double改成float是可以AC的,但有的代码AC不了。可能我是欧洲人?

    #include<cstdio>
    using namespace std;
    int const N=31,MOD=7e5+1,M=7e5+1;
    int n,k,ct,cc,lt;
    struct node{
        int head[MOD],Next[M],to[M],t;
        double ww[M];
        inline int find(int x){
            for(register int i=head[x%MOD];i;i=Next[i])
                if(to[i]==x)return i;
            return 0;
        }
        inline void ins(int x,double z){
            int now=x%MOD;
            to[++t]=x,ww[t]=z;
            Next[t]=head[now],head[now]=t;
            return ;
        }
    }Hash[N];
    inline double max(double x,double y){
        return x>y?x:y;
    }
    inline int del(int x,int y){
        return ((x&(lt-1<<y))>>1)|(x&((1<<y-1)-1));
    }
    double dfs(int x,int ak,int rm){
        if(x>k)return rm;
        int r=n-x+1,lit=r>>1,lp=1,rp=n,cp,now=ak&((1<<r)-1);
        if((cp=Hash[x].find(ak)))return Hash[x].ww[cp];
        double ans(0);
            for(int i=1;i<=lit;++i)
            ans+=2*max(dfs(x+1,del(ak,i),rm+((now>>i-1)&1)),
                dfs(x+1,del(ak,r-i+1),rm+((now>>r-i)&1)));
        if(r&1)ans+=dfs(x+1,del(ak,lit+1),rm+((now>>lit)&1));
        ans/=(double)r;
        Hash[x].ins(ak,ans);
        return ans;
    }
    int main(){
        scanf("%d%d",&n,&k),lt=1<<n;
        int st=0;
        char bb=getchar();
        while(bb^'W'&&bb^'B')bb=getchar();
        for(register int i=1;i<=n;++i,bb=getchar())
            if(bb=='W')++ct,st|=1<<i-1;
            else ++cc;
        if(!k || cc==n)return puts("0.0000000000"),0;
        if(k==n){
            printf("%d",ct);
            return puts(".0000000000"),0;
        }
        if(ct==n)return printf("%d.0000000000",k),0;
        printf("%.10lf",dfs(1,st,0));
        return 0;
    }  
    View Code

     T3

    大神级题目,思路很不错。

    题解请参考露迭月大神的博客。

    注意dp[i][0]是不向上伸出翻转边,dp[i][1]是伸出。

    时间复杂度$Theta(N)$。

    放一下我的代码:

    #include<cstdio>
    #include<iostream>
    #define mk make_pair
    #define fr first
    #define sc second
    using namespace std;
    typedef pair<int,int> pi;
    int const N=1e5+5,lar=1e9+7;
    const pi lp=mk(lar,lar),rp=mk(0,0);
    inline int read(){
        int ss=0;char bb=getchar();
        while(bb<48||bb>57)bb=getchar();
        while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
        return ss;
    }
    int n,ans,tot;
    int head[N],Next[N<<1],to[N<<1],lt[N<<1],t;
    pi f[N][2];
    inline void add(int x,int y,int z,int p){
        to[++t]=y,lt[t]=p==2?2:(z^p);
        Next[t]=head[x],head[x]=t;
        return ;
    }
    pi operator + (pi x,pi y){
        return mk(x.fr+y.fr,x.sc+y.sc);
    }
    void dfs(int x,int fa,int z){
        pi a=lp,b=rp,c,d;
        for(int i=head[x],y;i;i=Next[i])
            if((y=to[i])^fa){
                dfs(y,x,lt[i]);
                c=min(a+f[y][0],b+f[y][1]),d=min(a+f[y][1],b+f[y][0]);
                a=c,b=d;
            }
        f[x][0]=z==1?lp:min(mk(a.fr+1,a.sc),b);
        f[x][1]=z?min(mk(a.fr,a.sc+1),mk(b.fr+1,b.sc+1)):lp;
        return ;
    }
    int main(){
        n=read();
        for(register int i=1,ff,tt,zz,pp;i<n;++i)
            ff=read(),tt=read(),zz=read(),pp=read(),
            add(ff,tt,zz,pp),add(tt,ff,zz,pp);
        dfs(1,0,0);
        printf("%d %d",f[1][0].fr>>1,f[1][0].sc);
        return 0;
    }
    View Code
  • 相关阅读:
    挺好用的SQLSERVER数据库自动备份工具SQLBackupAndFTP(功能全面)
    SQLSERVER中的鬼影索引
    SQLSERVER NULL和空字符串的区别 使用NULL是否节省空间
    SQLSERVER中NULL位图的作用
    SQLSERVER到底能识别多少个逻辑CPU?
    不正常关机引起的数据库置疑
    如何在大型的并且有表分区的数据库中进行DBCC CHECKDB操作
    索引视图是否物理存储在数据库中以及使用索引视图的一些见解
    Oracle非重要文件恢复,redo、暂时文件、索引文件、password文件
    最大匹配、最小顶点覆盖、最大独立集、最小路径覆盖(转)
  • 原文地址:https://www.cnblogs.com/remarkable/p/11603028.html
Copyright © 2011-2022 走看看