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\n",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\n",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\n",dp[maxx-1]);
    return 0;
}
View Code

 

 

posted @ 2019-10-22 19:45  JBLee  阅读(99)  评论(0编辑  收藏  举报