zoukankan      html  css  js  c++  java
  • [NOIP模拟23]题解

    中间鸽了好几篇啊QAQ……有时间再补吧……

    A.mine

    sbdp,考场上写的巨麻烦不过还是能A的(虽然MLE了……每一维都少开1就A掉了555)。设$dp[i][j][k]$为枚举到第i位,第i位是j,第i-1位是k的方案数。j和k都是0~3的整数,分别代表有前后几个雷/就是雷。

    然后大力分类讨论即可

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int N=1e6+5;
    typedef long long ll;
    const ll mod=1e9+7;
    char s[N];
    int str[N];
    ll dp[N][5][5];//到第i位 第i位是j 第i-1位是k
    //0 0
    //1 1
    //2 2
    //3 *
    int len;
    void mmod(ll &x)
    {
        while(x>=mod)x=x-mod;
    }
    bool isbomb(int x)
    {
        if(str[x]==4||str[x]==3)return 1;
        return 0;
    }
    bool judge()
    {
        for(int i=1;i<=len;i++)
        {
            if(str[i]==4)continue;
            if(str[i]==1&&(str[i-1]==2||(str[i-1]==3&&str[i+1]==3)||(!isbomb(i-1)&&!isbomb(i+1))))return 0;
            if(str[i]==2&&(!isbomb(i-1)||!isbomb(i+1)))return 0;
        }
        return 1;
    }
    int main()
    {
        scanf("%s",s+1);len=strlen(s+1);
        for(int i=1;i<=len;i++)
            if(s[i]=='2')s[i-1]=s[i+1]='*';
        if(len==1)
        {
            if(s[1]=='*'||s[1]=='0')puts("1");
            else puts("0");
            return 0;
        }
        for(int i=1;i<=len;i++)
        {
            if(s[i]=='0')str[i]=0;
            else if(s[i]=='1')str[i]=1;
            else if(s[i]=='2')str[i]=2;
            else if(s[i]=='*')str[i]=3;
            else if(s[i]=='?')str[i]=4;
        }
        if(!judge())
        {
            puts("0");
            return 0;
        }
        if(str[1]!=4)
        {
            if(str[1]==1)dp[1][1][0]=1;
            else if(str[1]==3)dp[1][3][1]=1;
            else if(str[1]==0)dp[1][0][0]=1;
        }
        else dp[1][1][0]=dp[1][3][1]=dp[1][0][0]=1;
        for(int i=2;i<=len;i++)
        {
        //    cout<<str[i]<<endl;
            if(str[i]==0||str[i]==4)
            {
                mmod(dp[i][0][1]+=dp[i-1][1][3]);
                mmod(dp[i][0][0]+=(dp[i-1][0][1]+dp[i-1][0][0]));
            }
            if(str[i]==1||str[i]==4)
            {
                mmod(dp[i][1][3]+=dp[i-1][3][1]+dp[i-1][3][2]+dp[i-1][3][3]);
                mmod(dp[i][1][0]+=dp[i-1][0][1]+dp[i-1][0][0]);
                mmod(dp[i][1][1]+=dp[i-1][1][3]);
            }
            if(str[i]==2||str[i]==4)
            {
                mmod(dp[i][2][3]+=dp[i-1][3][2]+dp[i-1][3][1]+dp[i-1][3][3]);
            }
            if(str[i]==3||str[i]==4)
            {
                mmod(dp[i][3][1]+=dp[i-1][1][0]+dp[i-1][1][1]);
                mmod(dp[i][3][2]+=dp[i-1][2][3]);
                mmod(dp[i][3][3]+=dp[i-1][3][1]+dp[i-1][3][2]+dp[i-1][3][3]);
            }
        }
        ll ans=0;
        if(str[len]!=4&&str[len-1]!=4)ans=dp[len][str[len]][str[len-1]];
        else if(str[len]==4&&str[len-1]!=4)
        {
            for(int i=0;i<=3;i++)
            {
                int j=str[len-1];
                if(i==0&&(j==3||j==2))continue;
                if(i==1&&(j!=3))continue;
                if(i==2)continue;
                if(i==3&&j==0)continue;
                mmod(ans+=dp[len][i][str[len-1]]);
            }
        }
        else if(str[len]!=4&&str[len-1]==4)
        {
            for(int j=0;j<=3;j++)
            {
                int i=str[len];
                if(i==0&&(j==3||j==2))continue;
                if(i==1&&(j!=3))continue;
                if(i==2)continue;
                if(i==3&&j==0)continue;
                mmod(ans+=dp[len][str[len]][j]);
            }
        }
        else
        {
            for(int i=0;i<=3;i++)
                for(int j=0;j<=3;j++)
                {
                    if(i==0&&(j==3||j==2))continue;
                    if(i==1&&(j!=3))continue;
                    if(i==2)continue;
                    if(i==3&&j==0)continue;
                    mmod(ans+=dp[len][i][j]);
                }                
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    一开始那个$judge()$是判不合法情况。A掉之后突然发现我的程序好像遇到一开始就不合法的情况还是会有方案,所以特判了一下。

    B.water

    一个块的最终高度(初始高度+积水高度)就是从这个块走出矩形的路径上最大值的最小值,即把路径定义为权值取max而非权值和后的最短路。那么就直接相邻块建图,边界块都连到一个源点上,之后从源点跑dj即可。有点像网络流。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    #include<map>
    #define pa pair<int,int>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int N=305,M=500005;
    const int dx[5]={0,1,0,-1},
              dy[5]={1,0,-1,0};
    int n,m;
    int a[N][N];
    int to[M],nxt[M],head[M],len[M],tot;
    void add(int x,int y,int z)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        len[tot]=z;
        head[x]=tot;
    }
    int id(int i,int j){return (i-1)*m+j;}
    int dis[M],vis[M];
    void dj(int s)
    {
        memset(dis,0x3f,sizeof(dis));
        priority_queue<pa> q;
        dis[s]=0;q.push(make_pair(0,s));
        while(!q.empty())
        {
            int x=q.top().second;q.pop();
            if(vis[x])continue;
            vis[x]=1;
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(dis[y]>max(dis[x],len[i]))
                {
                    dis[y]=max(dis[x],len[i]);
                    q.push(make_pair(-dis[y],y));
                }
            }
        }
    }
    
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                a[i][j]=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                for(int k=0;k<4;k++)
                {
                    int nx=i+dx[k],ny=j+dy[k];
                    if(nx<1||nx>n||ny<1||ny>m)add(n*m+1,id(i,j),max(a[i][j],0)),add(id(i,j),n*m+1,max(a[i][j],0));
                    else add(id(i,j),id(nx,ny),max(a[i][j],a[nx][ny]));
                }
            }
        dj(n*m+1);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                printf("%d ",dis[id(i,j)]-a[i][j]);
            putchar('
    ');
        }
        return 0;
    }
    View Code

    C.gcd

    披着数据结构题的毒瘤数论。

    需要维护三个量:

    $f[]$:选中集合中gcd为i的数对个数

    $g[]$:选中集合中gcd为i的倍数的数对个数

    $s[]$:选中集合中i的倍数的个数

    首先$s[]$的值是很好得到的,每次加入数时枚举它的因子$i$,$s[i]++$即可。

    那$s[]$的意义何在呢?它可以帮助我们快速得到$g[]$。显然$g[i]=C_{s[i]}^2$。

    又因为$g[i]=sum limits_{i|d} f[d]$,可以用第二类莫比乌斯反演得到:

    $f[i]=sum limits_{i|d} mu (frac{d}{i}) g[d]$

    $f[1]$即为所求,所以每次更改选中状态时枚举更改的数的因子,更新它的$s[]$,进而更新它的$g[]$。

    ans可以通过减去原来的g再加上新g得到。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int N=200005,M=500005;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    typedef long long ll;
    bool vis[M];
    int pr[M],tot,mu[M];
    void ini()
    {
        mu[1]=1;
        for(int i=2;i<=M-5;i++)
        {
            if(!vis[i])pr[++tot]=i,mu[i]=-1;
            for(int j=1;j<=tot&&i*pr[j]<=M-5;j++)
            {
                vis[i*pr[j]]=1;
                if(i%pr[j])mu[i*pr[j]]=-mu[i];
                else
                {
                    mu[i*pr[j]]=0;
                    break;
                }
            }
        }
    }
    int n,m;
    int s[M],ctrl[N],a[N];
    int main()
    {
        n=read();m=read();
        ini();
        for(int i=1;i<=n;i++)
            a[i]=read();
        ll ans=0;
        while(m--)
        {
            int x=read();
            for(int i=1;i*i<=a[x];i++)
            {
                if(a[x]%i==0)
                {
                    if(!ctrl[x])ans+=1LL*mu[i]*s[i];
                    else ans-=1LL*(s[i]-1)*mu[i];
                    if(ctrl[x])s[i]--;
                    else s[i]++;
                    if(i*i==a[x])continue;
                    if(!ctrl[x])ans+=1LL*mu[a[x]/i]*s[a[x]/i];
                    else ans-=1LL*(s[a[x]/i]-1)*mu[a[x]/i];
                    if(ctrl[x])s[a[x]/i]--;
                    else s[a[x]/i]++;
                }
            }
            ctrl[x]^=1;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    MyBatis:分页的实现
    Mybatis之配置文件
    Java之创建线程的方式四:使用线程池
    Java之创建线程的方式三:实现Callable接口
    Java之线程通信的应用:经典例题:生产者/消费者问题
    Java之线程通信的方法
    Java之解决线程安全问题的方式三:Lock锁
    Java之同步方法处理实现Runnable接口的线程安全问题
    Java之同步方法处理继承Thread类的线程安全问题
    01 while 循环输入1 2 3 4 5 6 8 9 10
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11366491.html
Copyright © 2011-2022 走看看