zoukankan      html  css  js  c++  java
  • 模拟赛12-10

    感觉题目质量还是不错的

    第一题。。

    链接:https://www.luogu.org/problemnew/show/P3941

    题意:

    给出一个矩阵,求有多少个子矩阵元素和是p的倍数,p,a[i,j]<=1e6,n,m<=400;

    题解:

    首先看看暴力,确定矩形要o(n^4)的时候,计算要o(n^2),总的是o(n^6)

    考虑二维前缀和优化计算,时间是o(n^4)的

    似乎没有办法优化了

    考虑一下这个问题的子问题

    如果是一个一层的矩阵,也需要(n^2)的复杂度么

    显然答案是否定的

    区间[i,j]的和=sum[j]-sum[i-1],所以若要为k的倍数,即(sum[j]-sum[i-1])是k的倍数

    该条件等价于(sum[j]) mod k和(sum[i-1]) mod k 相同

    这样考虑到2维状态,我们可以先枚举出行,然后就变成了1维的问题

    另外有些细节要注意

    当枚举出行后,我们统计出sum[x] mod k的余数有哪些?有几个是这个余数?

    可以用一个大数组a[1000000+10000]来记录

    其次每次清除时只需要清除刚才修改过的地方,用memset会导致超时

    代码:

    ****本来是用p写的,后来发现被卡常了

    #include <bits/stdc++.h>
    using namespace std;
    long long p[500][500],sum[500][500];
    long long f[1000010],pos[1000010],a[1000010],ans;
    int main(){
        long long n,m,k;
        scanf("%lld%lld%lld",&n,&m,&k);
        for (long long i=1; i<=n; i++)
          for (long long j=1; j<=m;j++)
            scanf("%lld",&p[i][j]),sum[i][j]=(sum[i][j-1]+p[i][j])%k;
        for (long long i=1;i<=1000000; i++)
          f[i]=(i*(i-1))/2;
        for (long long i=1;i<=m;i++)
          for (long long j=i;j<=m;j++) 
          {
              long long num=0;
              for (long long u=0; u<=n;u++)
              {
                  long long  x=(sum[u][j]-sum[u][i-1]+k)%k;
                  num=(num+x+k)%k;
                  pos[u]=num;
                  a[num]++;
              }
              for (long long u=0;u<=n;u++) 
              ans=ans+f[a[pos[u]]], a[pos[u]]=0;
          }
        printf("%lld",ans);
        return(0); 
    }

    分析:代码难度不大,思维还是有难度的

    第二题:

    链接:https://www.luogu.org/problemnew/show/P3942

    题意:

    给出一颗树,每个点可控制距离l以内的点,求最少需要几个点
    题解:

    看完题目,觉得不是树dp就是点分治了

    后来想想好像做不了(当然k=1,2,3)拿部分分还是可以用树dp来做的

    还是说正解吧,是看了题解才知道的

    考虑对一个深度最深的节点,怎么样才能使得即能覆盖它又能对整颗树影响最大呢

    很显然,深度越浅越好啊,如果它能覆盖当前节点,它一定能覆盖它的所有子树,

    且对它的父亲以上及兄弟的节点相对于它的子树中的节点对他们的影响来说是最大的

    那现在的大体思路就是:找到它的k代父亲,再用它去更新距离<=k的节点

    另外思考一下 若x无k代父亲(即根节点到x的距离<k),那么我们应怎么办

    其实这解决起来很简单的,因为x是深度最深的节点,所以这种情况下让根节点去覆盖就能覆盖所有节点了

    另外对于维护当前深度最大值,可以刚开始弄出一个按dep排好序的数组

    把头节点标记在1,之后每访问到一个点,将其f[i]值设为false

    每次递增头结点直到找到为止,这个复杂度显然是o(n)的

    复杂度分析:感觉有点难算,不能确定每个点会被访问几次,但画一画就能发现不会有太多节点被访问很多次

    注意点:不要访问到f[i]=false就停止,举个例子x的某子孙之前覆盖了它,但没覆盖它3号儿子

    现在它的另一个子孙,能覆盖它的3号儿子

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 1000000
    struct re{int a,b,c;};
    int n,m,l,c,d,head[maxn],fa[maxn];
    bool f[maxn],ff[maxn];
    re a[maxn],dep[maxn];
    void arr(int x,int y)
    {
        l++;
        a[l].a=head[x];
        a[l].b=y;
        head[x]=l;
    };
    void dfs(int x,int y)
    {
        f[x]=false; dep[x].a=y; dep[x].b=x; 
        int u=head[x];
        while (u)
        {
            int v=a[u].b;
            if (f[v]) 
              fa[v]=x,dfs(v,y+1);
            u=a[u].a;
        }
    };
    bool cmp(re x,re y)
    {
        return(x.a>y.a);
    };
    void dfs2(int x,int y,int z)
    {
        ff[x]=false;
      if (!y) return;
        int u=head[x];
        while (u)
        {
            int v=a[u].b;
            if (v!=z) dfs2(v,y-1,x);
            u=a[u].a;
        }
    };
    int main(){
        int tmp; 
      cin>>n>>m>>tmp;
      for (int i=1;i<n;i++)
      cin>>c>>d,arr(c,d),arr(d,c);
      memset(f,true,sizeof(f));
      memset(ff,true,sizeof(ff));
      dfs(1,0);
      stable_sort(dep+1,dep+n+1,cmp);
      int h=1,ans=0;
      while (h<=n)
      {
        while (h<=n && !ff[dep[h].b]) h++;
        if (h<=n)
        { 
          int x=dep[h].b,y=m;
              while (fa[x] && y>0) x=fa[x],y--;
              if (y>0) 
                { 
                  ans++;
                  goto tp;
              }  
          dfs2(x,m,0),ans++;      
        }
      }
      tp:
      cout<<ans;
    }
  • 相关阅读:
    @雅礼集训01/13
    @hdu
    @bzoj
    @hdu
    @bzoj
    @雅礼集训01/10
    @codeforces
    @spoj
    @bzoj
    @bzoj
  • 原文地址:https://www.cnblogs.com/yinwuxiao/p/8016847.html
Copyright © 2011-2022 走看看