• [NOIP]模拟17 题解


    A.入阵曲

    部分分很肥,正解写得常数稍大就会和暴力一个分,考试的时候写什么自己考虑。(滑稽

    部分分的循环边界手抖写错了-25 (原本暴力分中的10分都没了啊啊啊)

    没写挂的话应该有75,其实就是二维前缀和+暴力枚举点对统计+$a[i][j]$都相等时只枚举子矩形大小再乘上这种大小出现的次数。

    正解:$(sum[r]-sum[l-1])\% K=0 ightarrow sum[r]\% K=sum[l-1]\% K$

    枚举行数$i,j$和列数$k$,维护i行和j行之间、k列左侧在%K意义下同余矩阵的个数,用桶来实现。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=650;
    int n,m;
    ll K,ans;
    int a[N][N];
    ll sum[N][N],sum1[N],cnt[1000005];
    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;
    }
    
    int main()
    {
        n=read();m=read();K=1LL*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++)
                sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+1LL*a[i][j];
        for(int i=0;i<n;i++)
            for(int j=i+1;j<=n;j++)
            {
                cnt[0]=1;
                for(int k=1;k<=m;k++)
                {
                    sum1[k]=(sum[j][k]-sum[i][k]+K)%K;
                    ans+=cnt[sum1[k]];
                    cnt[sum1[k]]++;
                }
                for(int k=1;k<=m;k++)cnt[sum1[k]]=0;
            }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    B.将军令

    我考场都能想到的sb贪心。每次取出深度最大且未被覆盖的点,在它的K级祖先上驻扎即可。前者用堆维护,后者直接暴力修改覆盖状态,注意向上修改时不要只遍历和它在一条链上的。

    正确性?读者自证不难。(逃

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    #define pa pair<int,int>
    #define re register
    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=100005;
    int n,K,t;
    int to[N<<1],nxt[N<<1],head[N],tot;
    inline void add(int x,int y)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    int dep[N],fa[N];
    priority_queue<pa> q;
    void dfs1(int x,int deep)
    {
        dep[x]=deep;
        for(re int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(!dep[y])dfs1(y,deep+1),fa[y]=x;
        }
        return ;
    }
    int cover[N],anc,ans;
    void getan(int x,int rest)
    {
        if(!rest||x==1)
        {
            anc=x;
            return ;
        }
        getan(fa[x],rest-1);
        return ;
    }
    void dfs2(int x,int rest,int f)
    {
        cover[x]=1;
        if(!rest)return ;
        for(re int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(y==f)continue;
            dfs2(y,rest-1,x);
        }
        return ;
    }
    
    int main()
    {
        n=read();K=read();t=read();
        for(re int i=1;i<n;i++)
        {
            int x=read(),y=read();
            add(x,y);add(y,x);
        }
        dfs1(1,1);
        for(re int i=1;i<=n;i++)
            q.push(make_pair(dep[i],i));
        while(!q.empty())
        {
            int x=q.top().second;
            q.pop();
            if(cover[x])continue;
            getan(x,K);
            //cout<<anc<<endl;
            dfs2(anc,K,-1);
            ans++;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    C.星空

    区间状态反转可以看作区间异或1,但区间操作不好处理,考虑通过差分转化为单点操作。即把对原数组上$[L,R]$区间的操作转化为差分数组上$L-1$和$R$两个点的操作。这里用到了异或差分:$dif[i]=a[i] xor a[i+1]$

    所以问题变成了:有一个01串,每次对其中两个点$xor 1$,需要多少次把这个串的每一位都变成0。(差分数组全0对应原数组全1)

    可以发现,消去两个1的费用与他们之间的距离有关。bfs预处理出来后,问题再次得到转化:每次取出一对物品,每对物品取出都有一定代价,如何取出使得代价最小。由于不亮的灯泡很少,这个问题完全可以状压解决。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    #include<vector>
    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=40005;
    int n,m,K;
    int a[N],op[N],dif[N];
    int pos[N],sz;
    int vis[N],dis[20][20],d[N];
    int find0[N<<1],dp[N<<1];
    void bfs(int node)
    {
        memset(d,0,sizeof(d));
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(node);
        vis[node]=1;d[node]=0;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=1;i<=m;i++)
            {
                int s1=x+op[i],s2=x-op[i];
                if(s1<=n&&!vis[s1])
                {
                    q.push(s1);
                    d[s1]=d[x]+1;
                    vis[s1]=1;
                }
                if(s2>=0&&!vis[s2])
                {
                    q.push(s2);
                    d[s2]=d[x]+1;
                    vis[s2]=1;
                }
            }
        }
    }
    
    int main()
    {
        n=read();K=read();m=read();
        for(int i=1;i<=K;i++)
            a[read()]=1;
        for(int i=1;i<=m;i++)
            op[i]=read();
        for(int i=0;i<=n;i++)
        {
            dif[i]=a[i]^a[i+1];
            if(dif[i])pos[++sz]=i;
        }
        for(int i=1;i<=sz;i++)
        {
            bfs(pos[i]);
            for(int j=1;j<=sz;j++)
                dis[i][j]=d[pos[j]];
        }
        for(int s=0;s<(1<<sz);s++)
        {
            find0[s]=sz-1;
            for(int i=0;i<sz;i++)
                if(((s>>i)&1)==0)
                {
                    find0[s]=i;
                    break;
                }
        }
        memset(dp,0x3f,sizeof(dp));
        dp[0]=0;
        for(int s=0;s<(1<<sz);s++)
            for(int i=find0[s]+1;i<sz;i++)
            {
                if(((s>>i)&1)==0&&dis[find0[s]+1][i+1])
                {
                    int now=(s|(1<<find0[s])|(1<<i));
                    dp[now]=min(dp[now],dp[s]+dis[find0[s]+1][i+1]);
                    //cout<<dp[now]<<endl;
                }
            }
        cout<<dp[(1<<sz)-1]<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    Apache Tomcat的安装与配置
    2007年东软校园招聘笔试题(山东大学)
    成功修复U盘,狂喜中~哈哈哈!!!
    检测和修复U盘的一般过程(一、软检测篇)
    在 hp3172宽屏笔记本上安装windowsXP+solaris10 双系统(1)--环境准备篇
    递归法查找假硬币
    把ppt转换为word的一段脚本
    2007年11月神州数码软件工程师(网络公司)招聘笔试题目
    不能正常使用oracle的解决办法(Oracle10g Express 版)
    光驱弹不出来怎么办?光驱弹不出来的简易解决办法。
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11342841.html
Copyright © 2020-2023  润新知