zoukankan      html  css  js  c++  java
  • 【2019.10.8】备战CSP2019模拟赛

    总结

    分少因为正解打得少。

    分不够多因为暴力打得不够多。

    (到位)

    T1.方阵

    题意:

    n*m的方阵,第i列j行的值为Xij,给定几个矩形的左上角和右下角,问SUM/ MAX/ MIN(询问的矩形较长边不会大于两倍较短边)?

    题解:

    我是打的前缀和求SUM,但是遇到MAX和MIN就直接跪了,没想到打RMQ,打的枚举,拿了40暴力分。

    正解矩阵求和用前缀和,矩阵最值二维RMQ维护,特殊处理下矩阵询问,转变为询问两个正方形,保证空间合法。

    f[i][j][k]表示左上角顶点坐标为(i, j),边长为2的k次方的正方形。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=801;
    string s;
    int a[N][N],sum[N][N],f[N][N][10],g[N][N][10],p[11];
    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 int write(int x)
    {
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline int max(int x,int y)
    {
        return x>y?x:y;
    }
    inline int min(int x,int y)
    {
        return x<y?x:y;
    }
    int main(){
        freopen("phalanx.in","r",stdin);
        freopen("phalanx.out","w",stdout);
        int n=read(),m=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                f[i][j][0]=g[i][j][0]=a[i][j]=read();
                sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
            }
        for(int i=p[0]=1;i<=10;i++) p[i]=p[i-1]<<1;
        for(int k=1;k<10;k++)
            for(int i=1;i<=n && i+p[k]-1<=n;i++)
                for(int j=1;j<=m && j+p[k]-1<=m;j++)
                {
                    f[i][j][k]=max(f[i][j][k-1],f[i][j+p[k-1]][k-1]);
                    f[i][j][k]=max(f[i][j][k],max(f[i+p[k-1]][j][k-1],f[i+p[k-1]][j+p[k-1]][k-1]));
                    g[i][j][k]=min(g[i][j][k-1],g[i][j+p[k-1]][k-1]);
                    g[i][j][k]=min(g[i][j][k],min(g[i+p[k-1]][j][k-1],g[i+p[k-1]][j+p[k-1]][k-1]));
                }
        int q=read();
        while(q--)
        {
            cin>>s;
            int x1=read()+1,y1=read()+1,x2=read()+1,y2=read()+1;
            if(s=="SUM")
            {
                write(sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]),putchar('
    ');
                continue;
            }
            int ans=s=="MIN"?3000:0,z=min(log2(x2-x1+1),log2(y2-y1+1));
            if(s=="MAX")
            {
                for(int yy=y1;yy+p[z]<=y2;yy+=p[z]) ans=max(ans,max(f[x1][yy][z],f[x2-p[z]+1][yy][z]));
                for(int xx=x1;xx+p[z]<=x2;xx+=p[z]) ans=max(ans,max(f[xx][y1][z],f[xx][y2-p[z]+1][z]));
                ans=max(ans,max(f[x1][y1][z],f[x1][y2-p[z]+1][z]));
                ans=max(ans,max(f[x2-p[z]+1][y1][z],f[x2-p[z]+1][y2-p[z]+1][z]));
            }else
            {
                for(int yy=y1;yy+p[z]<=y2;yy+=p[z]) ans=min(ans,min(g[x1][yy][z],g[x2-p[z]+1][yy][z]));
                for(int xx=x1;xx+p[z]<=x2;xx+=p[z]) ans=min(ans,min(g[xx][y1][z],g[xx][y2-p[z]+1][z]));
                ans=min(ans,min(g[x1][y1][z],g[x1][y2-p[z]+1][z]));
                ans=min(ans,min(g[x2-p[z]+1][y1][z],g[x2-p[z]+1][y2-p[z]+1][z]));
            }
            write(ans),putchar('
    ');
        }
        return 0;
    }
    View Code

     

    T2.合照

    题意:

    给出n个数,有一些特定要求,形如x,y表示x一定要在y的左边,求排列方案数。

    题解:

    第一眼想序列做法,失败。第二眼考虑状压DP,时间复杂度不够优秀,可能还要加优化(可是我想不到优化方法)。看完T3再回头,可以连边...树形DP??然而敲到最后都没时间打T3的白送20分了还是敲错了只拿到40分。部分分的做法不一定是正解的暴力。(实在想不到优化八成就不是)。

    “首先有一个条件就是每一个人最多提出一个要求。
    那么连边后的dag是一个森林,不相交的n棵树。
    g[x]*=g[v]*C(size[x],size[v]);
    表示以x为根的方案数,这个用乘法原理很好理解,每一颗子树是独立的。
    然后答案也是一样的,相当于有一个超级根把所有树连在一起,所以就有:
    ans*=g[i]*C(sz,size[i]);,同理,也是独立的,直接乘就好。 ”

    代码:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define fo(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    using namespace std;
    const int N=2e5+5;
    int head[N],next[N],go[N],size[N],d[N];
    int tot,fac[N],inv[N],n,m,mo,f[N],g[N];
    inline void add(int x,int y)
    {
        go[++tot]=y;
        next[tot]=head[x];
        head[x]=tot;
    }
    inline int find(int x)
    {
        if (f[x]==x) return x;
        else return f[x]=find(f[x]);
    }
    inline int C(int n,int m)
    {
        return 1ll*fac[n]*inv[m]%mo*inv[n-m]%mo;
    }
    inline void dfs(int x)
    {
        g[x]=1;size[x]=0;
        for (int i=head[x];i;i=next[i])
        {
            int v=go[i];
            dfs(v);
            size[x]+=size[v];
            g[x]=1ll*g[x]*g[v]%mo*C(size[x],size[v])%mo;
        }
        size[x]++;
    }
    int main()
    {
        freopen("photo.in","r",stdin);
        freopen("photo.out","w",stdout);
        int cas;
        scanf("%d",&cas);
        while(cas--)
        {
            scanf("%d%d%d",&n,&m,&mo);
            fac[0]=inv[0]=fac[1]=inv[1]=1;
            fo(i,2,n)
            fac[i]=1ll*fac[i-1]*i%mo,inv[i]=1ll*(mo-mo/i)*inv[mo%i]%mo;
            fo(i,2,n)
            inv[i]=1ll*inv[i]*inv[i-1]%mo;
            fo(i,1,n)f[i]=i,head[i]=d[i]=0;
            int flag=0;tot=0;
            while (m--)
            {
                int x,y;
                scanf("%d%d",&x,&y);
                if (find(x)!=find(y))f[find(x)]=find(y);
                else flag=1;
                add(y,x);
                d[x]++;
    
            }
            if (flag)
            {
                printf("0
    ");
                continue;
            }
            int ans=1,sz=0;
            fo(i,1,n)
            if (!d[i])
            {
                dfs(i);
                sz+=size[i];
    
                ans=1ll*ans*g[i]%mo*C(sz,size[i])%mo;
            }
            printf("%d
    ",ans);
        }
    }
    View Code

    T3.

    想到一个很不优的DP思路,一看就会超时, 没打,想着打完T2回头拿个20分暴力滚,转头肝T2去了。结果有人打了还拿了80分(加了优化)...所以我还是要加快肝题速度啊,才有时间打暴力。

  • 相关阅读:
    C# WinForm判断Win7下程序是否以管理员身份运行
    C#设置文件(夹)权限
    验证字符串是否为有效的IP地址
    内存对齐
    C#线程启动时传入参数
    写字符串内容到文件
    Thread.Abort() Is Evil.
    如何优雅地控制线程状态
    C#读取文件,返回字符串形式的文件内容
    关于产生不重复随机数的算法 C#
  • 原文地址:https://www.cnblogs.com/jian-song/p/11634925.html
Copyright © 2011-2022 走看看