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

    T1 入阵曲

    N的范围很小,可以用n^3的方法解决。但是我一开始想的是线段树维护矩阵的和,但是复杂度一直降不下来,还多一个log,最后还没有n^4的暴力高。

    N^4暴力(70分):就是二维前缀和,n^4枚举即可。

    100分做法:

    现预处理出二维前缀和,然后枚举行和列,枚举列可以直接变成少掉一维枚举,就是看是否能被整除,然后开一个桶统计一下就可以了,复杂度n^3。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=505;
    const int nn=1e6+7;
    int a[maxn][maxn];
    long long sum[maxn][maxn];
    int n,m,k;
    long long ans;
    int cnt[nn];
    int b[nn];
    int main(){
        freopen("rally.in","r",stdin);
        freopen("rally.out","w",stdout);
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
        }
        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]+a[i][j];
        }
        for(int i=0;i<n;i++){
            for(int j=i+1;j<=n;j++){
                cnt[0]=1;
                for(int o=1;o<=m;o++){
                    b[o]=(sum[j][o]-sum[i][o]+k)%k;
                    ans+=cnt[b[o]];
                    cnt[b[o]]++;
                }
                for(int o=1;o<=m;o++) cnt[b[o]]=0;
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    T2 将军令

    一看,这不是最小点覆盖吗,直接上树形dp最小点覆盖,结果最后wa的很惨。

    正解:树形dp(那是肯定的),只不过dp方程极其复杂,考虑有没有简单一点的方法,当然就是贪心。贪心的枚举每个点最多能覆盖掉的点的个数打标记,用一个堆来存,从深度深的点向它的第k祖先上打标记。看哪些点没被覆盖即可。如果没被覆盖,就增加一个驿站。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+7;
    struct node{
        int nxt,to;
    }edge[maxn*2];
    int head[maxn],cnt;
    int deg[maxn];
    bool vis[maxn];
    void add(int x,int y){
        edge[++cnt].nxt=head[x];
        edge[cnt].to=y;
        head[x]=cnt;
    }
    priority_queue<pair<int,int> >q;
    int fa[maxn];
    int dep[maxn];
    int tot;
    int n,m,k,x,y,t;
    void dfs1(int x){
        for(int i=head[x];i;i=edge[i].nxt){
            int v=edge[i].to;
            if(!dep[v]){
                dep[v]=dep[x]+1;
                fa[v]=x;
                dfs1(v);
            }
        }
    }
    void mark(int x,int f,int dep){
        vis[x]=true;
        if(dep==k) return;
        for(int i=head[x];i;i=edge[i].nxt){
            int v=edge[i].to;
            if(v==f) continue;
            mark(v,x,dep+1);
        }
    }
    int getfa(int x){
        int num=1;
        while(num<=k){
            x=fa[x];
            num++;
        }
        return x;
    }
    int main(){
        freopen("general.in","r",stdin);
        freopen("general.out","w",stdout);
        scanf("%d%d%d",&n,&k,&t);
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            add(x,y);add(y,x);
        }
        fa[1]=1;dep[1]=1;dfs1(1);
        for(int i=1;i<=n;i++) q.push(make_pair(dep[i],i));
        while(!q.empty()){
            int x=q.top().second;
            q.pop();
            if(vis[x]) continue;
            tot++;
            int kk=getfa(x);
            mark(kk,kk,0);
        }
        printf("%lld
    ",tot);
        return 0;
    }
    View Code

    T3 星空

    输出0,1,2,3运气最好的是输出2,有12分。

    看k的范围很小,就想是否可以状压处理,对于每次区间异或,可以将其转化为差分的形式,维护一个差分数组,然后看差分数组中每个不为0的点的位置所能到达的其他区间,预处理出他们之间的距离,bfs即可。然后就是状压dp。设dp[i]表示状态为i时的最小步数,我们的最终状态是灯全亮,就这样dp下去求解即可。转移方程也非常简单。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+7;
    const int N=550;
    int dis[20][maxn];
    int a[maxn],b[maxn];
    int cha[maxn];
    int n,k,m;
    int cnt[maxn],num;
    int x;
    bool vis[maxn];
    long long dp[1<<20|1];
    int state[N][N];
    void bfs(int x,int *dis){
        queue<int> q;
        memset(vis,false,sizeof(vis));
        q.push(x);
        dis[x]=0;
        vis[x]=1;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=1;i<=m;i++){
                int y=u-b[i];
                if(y>0&&!vis[y]){
                    dis[y]=dis[u]+1;
                    q.push(y);
                    vis[y]=true;
                }
                y=u+b[i];
                if(y<=n+1&&!vis[y]){
                    dis[y]=dis[u]+1;
                    q.push(y);
                    vis[y]=true;
                }
            } 
        }
    }
    int main(){
        freopen("starlit.in","r",stdin);
        freopen("starlit.out","w",stdout);
        scanf("%d%d%d",&n,&k,&m);
        memset(a,1,sizeof(a));
        for(int i=1;i<=k;i++){
            scanf("%d",&x);
            a[x]=0;
        }
        for(int i=1;i<=m;i++) scanf("%d",&b[i]);
        for(int i=1;i<=n+1;i++) cha[i]=a[i]^a[i-1];
        memset(dis,88,sizeof(dis));
        for(int i=1;i<=n+1;i++){
            if(cha[i]){
                cnt[++num]=i;
                bfs(i,dis[num]);
            }
        }
        memset(dp,88,sizeof(dp));
        for(int i=1;i<=num;i++){
            for(int j=1;j<=num;j++){
                state[i][j]=(1<<i-1)|(1<<j-1);
            }
        }
        dp[0]=0;
        int maxx=1<<num;
        for(int i=0;i<maxx;i++){
            for(int j=1;j<=num;j++){
                if((i&(1<<j-1))==0){
                    for(int k=j+1;k<=num;k++){
                        if((i&(1<<k-1))==0){
                            dp[i|(state[j][k])]=min(dp[i|state[j][k]],dp[i]+dis[j][cnt[k]]);
                        }
                    }
                }
            }
        }
        printf("%lld
    ",dp[maxx-1]);
        return 0;
    }
    View Code

  • 相关阅读:
    大数据概述
    递归下降语法分析
    消除左递归c语言文法
    自动转换机
    简单的C语言文法
    实验报告一 词法分析程序
    组合数据类型练习
    Python绘制五星红旗
    熟悉常用Linux操作
    大数据概述
  • 原文地址:https://www.cnblogs.com/LJB666/p/11722042.html
Copyright © 2011-2022 走看看