【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分(加了优化)...所以我还是要加快肝题速度啊,才有时间打暴力。

原文地址:https://www.cnblogs.com/jian-song/p/11634925.html