zoukankan      html  css  js  c++  java
  • ZOJ Monthly, March 2013 总结

    A.A Simple Tree Problem

      刚开始没想好怎么转化,其实,用vector记录下来每个父节点的直接孩子,来个深度优先遍历进行编号,就可以把树形的结构转化成一维的线性结构,然后就是一般的线段树了!

    ZOJ 3686
    //http://www.cnblogs.com/SolarWings/archive/2013/04/01/2994548.html
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    vector<int>son[100010];
    int l[100010],r[100010];//记录每个节点所对应的区间的左右端点
    int sum[400010],flag[400010];
    int num;
    
    void dfs(int x)
    {
        l[x]=num++;
        int size=son[x].size();
        for(int i=0;i<size;i++)
        {
            dfs(son[x][i]);
        }
        r[x]=num-1;
    }
    
    void pushup(int rt)
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
    
    void pushdown(int rt,int len,int llen)
    {
        flag[rt]=0;
        flag[rt<<1]^=1;
        flag[rt<<1|1]^=1;
        sum[rt<<1]=llen-sum[rt<<1];
        sum[rt<<1|1]=len-llen-sum[rt<<1|1];
        
    }
    
    void update(int s,int e,int l,int r,int rt)
    {
        if(s<=l&&e>=r)
        {
            flag[rt]^=1;
            sum[rt]=r-l+1-sum[rt];
            return;
        }
        int m=(l+r)>>1;
        if(flag[rt])
            pushdown(rt,r-l+1,m-l+1);
        if(m>=s)
            update(s,e,lson);
        if(m<e)
            update(s,e,rson);
        pushup(rt);
    }
    
    int query(int s,int e,int l,int r,int rt)
    {
        if(s<=l&&e>=r)
            return sum[rt];
        int ret=0;
        int m=(l+r)>>1;
        if(flag[rt])
            pushdown(rt,r-l+1,m-l+1);
        if(m>=s)
            ret+=query(s,e,lson);
        if(m<e)
            ret+=query(s,e,rson);
        pushup(rt);
        return ret;
    }
    
    int main()
    {
        int n,m,temp,i;
        char ch[10];
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(i=1;i<=n;i++)
            {
                son[i].clear();
            }
            for(i=2;i<=n;i++)
            {
                scanf("%d",&temp);
                son[temp].push_back(i);
            }
            num=1;
            dfs(1);
            memset(sum,0,sizeof(sum));
            memset(flag,0,sizeof(flag));
            while(m--)
            {
                scanf("%s %d",ch,&temp);
                if(ch[0]=='o')
                {
                    update(l[temp],r[temp],1,n,1);
                }
                else
                {
                    printf("%d\n",query(l[temp],r[temp],1,n,1));
                }
            }
            printf("\n");
        }
        return 0;
    }

    B.The Review Plan I

      其实从推导来说,这道题比C要水多了...甚B至根本就不用推导,直接用容斥就行了,比赛的时候一直想着,把每个位置不能放的点用vector存下来,但是这样大大增加了容斥原理实现的难度,当我们求有两个位置放错的情况数时,如果这两个位置都不能放X号章节,那么情况数求起来就会很麻烦,直到最后都没得出一个可用的公式..

      正确的思路是,记录m个条件,直接对这些条件进行容斥原理,但是有一个额外的条件,就是不能同时选对同一个位置的限制或者对同一个章节的限制,这里我加了两个check数组来记录...然后剩下的就是最裸的容斥了。

      还有很坑爹的一点!!!限制是可能重复的,要加判重!!

    ZOJ 3687
    //http://www.cnblogs.com/SolarWings/archive/2013/04/01/2994548.html
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    
    using namespace std;
    
    #define MOD 55566677
    
    int check[55],check2[55];//标记天数和章节
    int n,m,mark[55][55];
    long long fa[100],ans;
    struct node
    {
        int a,b;
    }va[30];
    
    void dfs(int pos,int num)
    {
        if(pos==(m+1))
        {
            if(num&1)
                ans=(ans-fa[n-num])%MOD;
            else
                ans=(ans+fa[n-num])%MOD;
        }
        else
        {
            dfs(pos+1,num);
            if(!check[va[pos].a]&&!check2[va[pos].b])
            {
                check[va[pos].a]=1;
                check2[va[pos].b]=1;
                dfs(pos+1,num+1);
                check[va[pos].a]=0;
                check2[va[pos].b]=0;
            }
        }
    }
    int main()
    {
        int i;
        fa[0]=1;
        for(i=1;i<=60;i++)
        {
            fa[i]=(fa[i-1]*i)%MOD;
        }
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            memset(mark,0,sizeof(mark));
            for(i=1;i<=m;i++)
            {
                scanf("%d%d",&va[i].a,&va[i].b);
                if(!mark[va[i].a][va[i].b])
                {
                    mark[va[i].a][va[i].b]=1;
                }
                else
                {
                    i--;
                    m--;
                }
            }
            ans=0;
            dfs(1,0);//(当前条件的编号,选中的条件个数)
            printf("%lld\n",(ans%MOD+MOD)%MOD);
        }
        return 0;
    }

    C.The Review Plan II

      容斥原理,推起来比较难,研究了好久才弄出来,假设有1个章节所在的天数是错误的情况数是W(1),则每个错误的章节i可以放在i或i+1天,剩下的章节方法数时(n-1)!

    所以w(1)=C(n,1)*2*(n-1)!,这一步很容易想到,有点坑的是2个以上的情况,假设有k个章节所在的天数是错误的情况数是W(K),总共n个章节分别记为1,2,...n,它们可放的天数可以表示为(1,2)(2,3)(3,4)...(n,1)现在我们把括号去掉即得到一个数列1,2,2,3,3,4,....n,1,现在我们从里面取出K个数,分别表示k个章节所在的天数,只要满足(1).取出的任何2数不相邻 (即不能取自同一个括号,且不会取到相同的数) (2)两个1不同时被取到。那么,剩下的元素的摆放方法就是(n-k)!,现在只要求出满足这两个条件的取法数就可以了。

      把问题单独拿出来,现在我们求一个子问题,1,2,3,....n中能够取出多少个长为k的递增子序列a1,a2...ak,使得ai+1-ai>1

      设已经取出了满足上述条件的子序列,我们构造一个新的序列,b1,b2....bk,其中bi=ai-(i-1);

      则bi+1-bi=ai+1-ai-1>0

      从而1<=a1=b1<b2<.....<bk<=n-k+1

      确定b序列只需要从1,2...n-k+1中取出k个数就行了,然后b序列又可以转化成对应的a序列,所以方法数是C(n-k-1,k)

      回到我们本来的问题,满足第一个条件的取法数是C(2n-k+1,k),满足第一个条件,但不满足第二个条件的取法数是C(2n-4-(k-2)+1,k-2)

      所以W(K)={C(2n-k+1,k)-C(2n-4-(k-2)+1,k-2)}*(n-k)!

      W(K)确定以后就是最裸的容斥原理了~

    ZOJ 3688
    //http://www.cnblogs.com/SolarWings/archive/2013/04/01/2994548.html
    #include<stdio.h>
    #define MOD 1000000007
    long long fa[200010],invfa[200010];
    
    
    long long quickpow(long long m,long long n,long long p)
    {
        long long ans=1;
        while(n)
        {
            if(n&1)
                ans=(ans*m)%p;
            n=n>>1;
            m=(m*m)%p;
        }
         return ans;
    }
     
    long long Inv(long long x,long long p)
    {
        return quickpow(x,p-2,p);
    }
    
    void init()
    {
        fa[0]=1;
        for(int i=1;i<=200000;i++)
        {
            fa[i]=(fa[i-1]*i)%MOD;
            invfa[i]=Inv(fa[i],MOD);
        }
        invfa[0]=Inv(fa[0],MOD);
    }
    
    long long W(int n,int k)
    {
        long long a=fa[2*n-k+1];
        a=(a*invfa[k])%MOD;
        a=(a*invfa[2*n-k-k+1])%MOD;
        long long b=fa[2*n-k-1];
        b=(b*invfa[k-2])%MOD;
        b=(b*invfa[2*n-2*k+1])%MOD;
        return ((fa[n-k]*(a-b))%MOD+MOD)%MOD;
    }
    
    int main()
    {
        init();
        int n,i;
        while(scanf("%d",&n)!=EOF)
        {
            if(n==1)
            {
                printf("0\n");
                continue;
            }
            long long ans=fa[n];
            for(i=1;i<=n;i++)
            {
                if(i&1)
                    ans=(ans-W(n,i))%MOD;
                else
                    ans=(ans+W(n,i))%MOD;
            }
            printf("%lld\n",(ans+MOD)%MOD);
        }
        return 0;
    }

    D.Digging

      比赛的时候不知道为什么一直没看过这道题,直到很多人过了,我们才去看,刚开始也没有思路,觉得很像背包,但是又不像背包,后来ry直接想到了交叉排序...目测这次比赛基本全靠ry= =,我是多能打酱油...

      假设要做任务1和2,现在的时间是T,如果先做1,得到的回报是V1=T*s1+(T-t1)*s2,如果先做2得到的回报是V2=T*s2+(T-t2)*s1,V1-V2=t2*s1-t1*s2,因此只要每两个元素都交叉比较来排序就可以得到最优的顺序,考虑到最后快结束的时候,不一定连续取就最优,所以在这个顺序下再用一次01背包就可以了。

      

    ZOJ 3689
    //http://www.cnblogs.com/SolarWings/archive/2013/04/01/2994548.html
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    int dp[10100];
    
    struct node
    {
        int s,t;
    }va[3010];
    
    int cmp(const void *a,const void *b)
    {
        return (*(node *)a).s*(*(node *)b).t-(*(node *)b).s*(*(node *)a).t;
    }
    
    int MAX(int x,int y)
    {
        return x>y?x:y;
    }
    
    int main()
    {
        int n,t,i,j;
        while(scanf("%d%d",&n,&t)!=EOF)
        {
            for(i=0;i<n;i++)
            {
                scanf("%d%d",&va[i].t,&va[i].s);
            }
            qsort(va,n,sizeof(va[0]),cmp);
            memset(dp,0,sizeof(dp));
            for(i=0;i<n;i++)
            {
                for(j=t;j>=va[i].t;j--)
                {
                    dp[j]=MAX(dp[j],dp[j-va[i].t]+j*va[i].s);
                }
            }
            printf("%d\n",dp[t]);
        }
        return 0;
    }

    E.Choosing number

      这道题也勉强算是数学题吧,居然没一下想到矩阵...好失败ToT

      正确的思路是,对于每一个方法数ans[n],分成2部分,一种是ans[n][0],代表n个人排成的,以号码数小于等于K的人结尾的方法数,ans[n][1]代表n个人排成的,以号码数大于K的人结尾的方法数所以:

      ans[n+1][0]=ans[n][0]*(k-1)+ans[n][1]*k

      ans[n+1][1]=(ans[n][0]+ans[n][1])*(m-k)

    这两个式子刚好可以构造一个2X2的矩阵,剩下的只要用矩阵的快速幂就OK了

    ZOJ 3690
    //http://www.cnblogs.com/SolarWings/archive/2013/04/01/2994548.html
    #include<stdio.h>
    #define MOD 1000000007
    #define maxn 6
    long long ret[maxn][maxn];
    long long init[maxn][maxn];
    long long buf[maxn][maxn];
    void matrixMul(long long a[][maxn] , long long b[][maxn] , long long n,long long mod)
    {
        long long i,j,k;
        for(i=0;i<n;i++)
        {
            for(j=0;j<n;j++)
            {
                buf[i][j]=0;
            }
        }
        for(i=0;i<n;i++)
        {
            for(k=0;k<n;k++)
            {
                if(a[i][k]==0)
                    continue;
                for(j=0;j<n;j++)
                {
                    if(b[k][j]==0)
                        continue;
                    buf[i][j]+=a[i][k]*b[k][j];
                    if (buf[i][j]>=mod||buf[i][j]<=-mod)
                    {
                        buf[i][j]%=mod;
                    }
                }
            }
        }
        for(i=0;i<n;i++)
        {
            for(j=0;j<n;j++)
            {
                a[i][j]=buf[i][j];
            }
        }
    }
    
    void matrixMul(long long n,long long m,long long mod)
    {
        long long i,j;
        for(i=0;i<n;i++)
        {
            for(j=0;j<n;j++)
            {
                ret[i][j]=(i==j);
            }
        }
        for(;m;m>>=1)
        {
            if(m&1)
            {
                matrixMul(ret,init,n,mod);
            }
            matrixMul(init,init,n,mod);
        }
    }
    
    int main()
    {
        int n,m,k;
        while(scanf("%d%d%d",&n,&m,&k)!=EOF)
        {
            init[0][0]=k-1;
            init[0][1]=m-k;
            init[1][0]=k;
            init[1][1]=m-k;
            matrixMul(2,n-1,MOD);
            long long ans=(k*ret[0][0]+(m-k)*ret[1][0])%MOD;
            ans=(ans+k*ret[0][1]+(m-k)*ret[1][1])%MOD;
            printf("%lld\n",ans);
        }
        return 0;
    }

    H.Happy Great BG

      其实这题也不能说是精度问题...主要两点易错的是

      1.教练俩人也是人(这个直接看出来了...因为样例都过不了)

      2.考虑到现实情况,不能直接保留2位小数,只要分以下不是0就要进位,所以后面要加个0.00499!确实很好地结合了实际= =

    ZOJ 3693
    //http://www.cnblogs.com/SolarWings/archive/2013/04/01/2994548.html
    #include<stdio.h>
    int main()
    {
        int n,k;
        double w;
        while(scanf("%d%lf%d",&n,&w,&k)!=EOF)
        {
            n+=2;
            int temp=n/k;
            n-=temp;
            printf("%.2lf\n",1.0*n*w/2+0.00499);
        }
        return 0;
    }

      

      

  • 相关阅读:
    面向对象设计技巧[Object Oriented Design Tips] 2
    面向对象设计的技巧[Object Oriented Design Tips]1
    36家示范性软件学院验收的得分排名顺序
    解决windows系统乱码(其实是法语)
    [maven] maven/appfuse 常用命令介绍
    [plsql] win7/64位 PL/SQL登录时报 ora12154无法解析指定的连接标识
    [maven] pom.xml常用配置介绍
    web.xml中classpath:和classpath*:的区别
    [http] 深入理解HTTP消息头
    [Hibernate] Hibernate连接mysql示范
  • 原文地址:https://www.cnblogs.com/SolarWings/p/2994548.html
Copyright © 2011-2022 走看看