zoukankan      html  css  js  c++  java
  • NOIP模拟测试17

    T1:入阵曲

      题目大意:给定一个N*M的矩形,问一共有多少个子矩形,使得矩形内所有书的和为k的倍数。

      60%:N,M<=80

        枚举矩形的左上角和右下角,用二维前缀和求出数字之和。

        时间复杂度$O(N^4)$

      100%

        我们发现美剧每个矩形的复杂度已经为N4,那么我们改为枚举矩形的边界。先枚举左右边界,再从上到下枚举下边界,两个矩形相减便可得出所有矩形,枚举时将每个矩形的区间和压入桶中,每次枚举时直接在桶中查询,左右边界更换时清桶。注意下边界要从0开始枚举。

        时间复杂度$O(N^3)$

        记得要卡常。

    Code:

     1 #include<iostream>
     2 #include<cstdio>
     3 const int L=1<<20|1;
     4 char buffer[L],*S,*T;
     5 #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
     6 #define LL unsigned long long
     7 #define rint register unsigned int
     8 using namespace std;
     9 const unsigned int N=410;
    10 int n,m,d;
    11 int s[N][N];
    12 unsigned int dfn[2000010],t[2000010];
    13 inline int read()
    14 {
    15     rint s=0;char c=getchar();
    16     while(c<48||c>57) c=getchar();
    17     while(c>=48&&c<=57)s=(s<<3)+(s<<1)+(c^48),c=getchar();
    18     return s;
    19 }
    20 int main()
    21 {
    22     n=read();m=read();d=read();
    23     for(rint i=1;i<=n;++i){
    24         for(rint j=1;j<=m;++j)
    25             s[i][j]=(s[i-1][j]+s[i][j-1]-s[i-1][j-1]+read())%d;
    26     }
    27     LL ans=0;rint df=0;
    28     for(rint i=1;i<=m;++i){
    29         for(rint j=i;j<=m;++j){
    30             dfn[0]=++df;t[0]=1;
    31             for(rint k=1;k<=n;++k){
    32                 register int now=((s[k][j]-s[k][j-i])%d+d)%d;
    33                 if(dfn[now]!=df){
    34                     dfn[now]=df;t[now]=1;
    35                 }
    36                 else{
    37                     ans+=t[now];++t[now];
    38                 }
    39             }
    40         }
    41     }
    42     printf("%lld
    ",ans);
    43     return 0;
    44 }
    T1

    T2:将军令:

      题目大意:给定一棵树,和一个距离K,在某个节点驻扎军队可以占领所有与该节点距离不超过K的点,求最少要驻扎多少军队。

      此题和小胖守皇宫类似,不过权值均为一,可以用贪心做。

      将所有的点按深度排序,从前往后扫,遇到未标记的点,则从他的K辈祖先处开始进行dfs,暴力标记,将ans加一,扫到最后的结果即是答案。

      贪心策略证明:

        由于每次找的是深度最大的点,那么每次dfs一定能标记到当前子树的所有儿子,同时要想使其他子树需要的军队尽可能少,则应再次条件下尽可能向上,越向上越能标记更多的点。证明完毕,贪心策略正确。

      时间复杂度$O(NK)$

    Code:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=100010;
     6 int n,k,t,m=0,ans=0;
     7 int fi[N],d[N],f[N];
     8 bool v[N];
     9 struct edge{
    10     int v,ne;
    11 }e[N<<1];
    12 struct point{
    13     int d,id;
    14 }p[N];
    15 void add(int x,int y)
    16 {
    17     e[++m].v=y;
    18     e[m].ne=fi[x];fi[x]=m;
    19 }
    20 bool comp(const point a1,const point a2)
    21 {
    22     return a1.d>a2.d;
    23 }
    24 int read()
    25 {
    26     int s=0;char c=getchar();
    27     while(c<'0'||c>'9') c=getchar();
    28     while(c>='0'&&c<='9'){
    29         s=(s<<3)+(s<<1)+c-'0';
    30         c=getchar();
    31     }
    32     return s;
    33 }
    34 void dfs(int x,int pa)
    35 {
    36     p[x].id=x;
    37     for(int i=fi[x];i!=0;i=e[i].ne){
    38         int y=e[i].v;
    39         if(y==pa) continue;
    40         f[y]=x;
    41         p[y].d=p[x].d+1;
    42         dfs(y,x);
    43     }
    44 }
    45 void work(int x,int pa,int d)
    46 {
    47     v[x]=true;
    48     if(d==0) return;
    49     for(int i=fi[x];i!=0;i=e[i].ne){
    50         int y=e[i].v;
    51         if(y==pa) continue;
    52         work(y,x,d-1);
    53     }
    54 }
    55 int main()
    56 {
    57     n=read();k=read();t=read();
    58     for(int i=1;i<n;i++){
    59         int x=read(),y=read();
    60         add(x,y);add(y,x);
    61     }
    62     p[1].d=0;dfs(1,0);
    63     sort(p+1,p+n+1,comp);
    64     for(int i=1;i<=n;i++){
    65         int x=p[i].id;
    66         if(!v[x]){
    67             for(int i=1;i<=k&&f[x]!=0;i++)
    68                 x=f[x];
    69             work(x,0,k);ans++;
    70         }
    71     }
    72     printf("%d
    ",ans);
    73     return 0;
    74 }
    T2

    T3:星空

      题目大意:给一个0/1串,每次能将一个给定长度的区间去反,求最少要几次将整个串变为零。

      0/1串长度:N<=40000,区间长度数:M<=64,1的个数:K<=8

      24%做法:N<=16

        直接状压,跑BFS即可。DFS会T飞

      复杂度$O(N*2^N)$

      100%做法:

        我们发现相对于N,K很小,可以用来做做文章。

      开始转化题意:

        我们设灯亮为0,灯灭为1,则序列全为0时,所有灯都点亮。

        不难发现当N很大时,该序列中仅有K个1,我们从0位开始将每一位与其后一位亦或,则最多有2K个数与前一位不同,最多有2K个1,每次选择一段区间[L,R]取反,仅会改变第L-1位和第R位的值,相当于将序列中一定距离的点同时取反。

      题意转化为:给一个0/1串,每次选择相距给定长度的点同时取反,最少几次能将整个串全变为0。

      若选择的两个书均为0,没有任何意义;

      若选择的两个数均为1,则相互抵消;

      若其中一个数为0,另一个数为1,相当与将1移动一定距离。

      题意再次转化为:数轴上有最多2K个点,每次将一个点移动一段距离,两个点碰在一起后同时消失,最少几次能让所有点消失。

      此时K的范围状压可以接受,BFS算出两点间移动的最少次数,用状压DP简单解决。

      时间复杂度$O(MNK+K*2^K)$或$O(MNK+K^2*2^K)$

    Code:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<queue>
     5 using namespace std;
     6 const int N=40010;
     7 const int inf=99999999;
     8 int n,k,m,tot=0;
     9 int a[N],b[N],v[N],c[20],d[20][20],e[100];
    10 int dp[1<<16];
    11 queue<int> q;
    12 int read()
    13 {
    14     int s=0;char c=getchar();
    15     while(c<'0'||c>'9') c=getchar();
    16     while(c>='0'&&c<='9'){
    17         s=(s<<3)+(s<<1)+c-'0';
    18         c=getchar();
    19     }
    20     return s;
    21 }
    22 void bfs(int pos)
    23 {
    24     int now=c[pos];
    25     memset(v,0,sizeof(v));
    26     v[now]=1;q.push(now);
    27     while(!q.empty()){
    28         int x=q.front();q.pop();
    29         for(int i=1;i<=m;i++){
    30             int y=x+e[i];
    31             if(y<=n+1&&v[y]==0){
    32                 v[y]=v[x]+1;q.push(y);
    33             }
    34             y=x-e[i];
    35             if(y>=1&&v[y]==0){
    36                 v[y]=v[x]+1;q.push(y);
    37             }
    38         }
    39     }
    40     for(int i=1;i<=tot;i++){
    41         if(i!=pos){
    42             if(v[c[i]]==0) d[pos][i]=inf;
    43             else d[pos][i]=v[c[i]]-1;
    44         }
    45     }
    46 }
    47 int main()
    48 {
    49     n=read();k=read();m=read();
    50     for(int i=1;i<=k;i++){
    51         int x=read();
    52         a[x]=1;
    53     }
    54     for(int i=0;i<=n;i++){
    55         b[i]=a[i]^a[i+1];
    56         if(b[i]==1) c[++tot]=i+1;    
    57     }
    58     for(int i=1;i<=m;i++)
    59         e[i]=read();
    60     for(int i=1;i<=tot;i++)
    61         bfs(i);
    62     for(int i=1;i<(1<<tot);i++)
    63         dp[i]=inf;
    64     for(int i=0;i<(1<<tot);i++){
    65         for(int j=1;j<=tot;j++){
    66             if(((i>>(j-1))&1)==1) continue;
    67             for(int g=1;g<=tot;g++){
    68                 if(g==j) continue;
    69                 if(((i>>(g-1))&1)==1) continue;
    70                 dp[i|(1<<(j-1))|(1<<(g-1))]=min(dp[i|(1<<(j-1))|(1<<(g-1))],dp[i]+d[j][g]);
    71             }
    72         }
    73     }
    74     printf("%d
    ",dp[(1<<tot)-1]);
    75     return 0;
    76 }
    T3

     

  • 相关阅读:
    【Services】【Web】【tomcat】配置tomcat支持https传输
    【Services】【Web】【apr】安装apr
    【Services】【Web】【Nginx】静态下载页面的安装与配置
    【Linux】【Problems】在fedora 9上解决依赖问题
    【Java】【设计模式】单例设计模式
    【Linux】【Shell】【text】awk
    【Linux】【Shell】【Basic】字符串操作
    【Linux】【Shell】【Basic】数组
    Linux上常用插件的一些命令(十)
    常见HTTP状态码
  • 原文地址:https://www.cnblogs.com/hz-Rockstar/p/11337593.html
Copyright © 2011-2022 走看看