8.11 NOIP模拟测试17 入阵曲+将军令+星空

T1 入阵曲

前缀和维护可以得60分 f[x1][y1][x2][y2]=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];  O(n4)

如果同一行的两个前缀和在模k意义下相等,那么他们之间的数的和一定是k的整数倍。把余数拿桶存起来,每次查询之前相同余数的有几个,直接加上。把一行拓展成许多行。

枚举矩阵的左右端点,中间的连续几列压成一列,(然后可以把纸旋转90°)就跟一行的一样了。复杂度O(n3)

需要注意的一点是初始化桶t[0]=1 因为如果这一整块本来就在模k意义下为0,用前缀和求这一段的和需要q[x]-q[0],而q[0]=0,需要把他也放进去。

桶里的东西怎么放进去的怎么拿出来。

ans要开long long

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
int n,m,k,a[410][410],sum[410][410],t[1001000],q[410];
ll ans;
int read()
{
    int aa=0,bb=1;char cc=getchar();
    while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
    while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^48);cc=getchar();}
    return aa*bb;
}
int main()
{
    n=read();m=read();k=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j]=read()%k;
            sum[i][j]=(sum[i][j-1]+a[i][j])%k;
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=i;j<=m;j++){
            q[0]=0;t[q[0]]=1;
            for(int l=1;l<=n;l++){
                q[l]=(q[l-1]+sum[l][j]-sum[l][i-1]+k)%k;
                ans+=t[q[l]];
                t[q[l]]++;
            }
            for(int l=1;l<=n;l++) t[q[l]]=0;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
入阵曲

 

T2 将军令

贪心

找到深度最深的节点,向上找距离他为k的节点,没有就是根,在这驻扎一定优于在这下面的节点,因为越往上他的拓展性越强。

所以每次找到没有被控制的最深的节点,向上找到能控制他且距离最远的祖先,控制他,祖先点亮造成的其他儿子被控制,直接dfs找到并标记,统计答案。

每次贪心的题都不会太难,难的是能想到贪心并且正确实现。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
struct node
{
    int to,nxt;
}h[200100];
int n,k,t,tot,ans,nxt[200100],dep[200100],f[100100];
priority_queue< pair<int,int> >q;
bool vis[100100],flag;
vector<int>ve[100100];
void add(int x,int y)
{
    h[++tot].to=y;
    h[tot].nxt=nxt[x];
    nxt[x]=tot;
}
void dfs(int x)
{
    for(int i=nxt[x];i;i=h[i].nxt){
        int y=h[i].to;
        if(dep[y]) continue;
        dep[y]=dep[x]+1;
        q.push(make_pair(dep[y],y));
        f[y]=x;
        dfs(y);
    }
}
void dfs1(int x,int d,int fa)
{
    if(d>=k) return;
    for(int i=nxt[x];i;i=h[i].nxt){
        int y=h[i].to;
        if(y==fa) continue;
        vis[y]=1;
        dfs1(y,d+1,x);
    }
}
int main()
{
    scanf("%d%d%d",&n,&k,&t);
    int u,v;
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    q.push(make_pair(1,1));
    dep[1]=1;
    dfs(1);
    while(q.size()){
        int d=q.top().first,x=q.top().second;
        q.pop();
        if(!vis[x]){
            int fa=x;
            for(int i=1;i<=k;i++){
                if(fa==1) break;
                fa=f[fa];
            }
            vis[fa]=1;
            dfs1(fa,0,0);
            ans++;
        }
    }
    printf("%d\n",ans);
    return 0;
}
将军令

 

T3 星空

卡了一下午,有点难过

把亮不亮抽象成01串,没点亮为1,点亮为0(反过来不好做)。区间取反可以用差分单点修改。c[i]=a[i]^a[i+1] c数组为差分数组,下标从0开始。

我们要的最终状态是所有灯都点亮,即全部为0,对应到差分数组上也是全部为0。

因为一共有k个没被点亮,和左边的0会在差分数组里得到一个1,右边同理也会得到一个。所以差分数组里最多有2*k个1。

区间翻转操作在差分数组里就变成了单点修改。翻转[2,4],就是单点修改1和4。

下面的操作均在差分数组上进行

假设我们操作的区间的端点是0和1,把0变成1,1变成0,及相当于1跳了b[i]的距离去找了0,代价为1;如果两个1相遇了,那么就全部翻转,全部变为0(消没了)。所以这就是1跳一跳去找另一个1的问题。。。

考虑在序列上建边,每个点跑b[i]的距离能到达哪,跑最短路。

剩下的就是状压了,把2*k个1状压,看那两个点匹配去消除。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
struct node
{
    int to,nxt;
}h[30100000];
int n,k,m,a[40010],b[40001],c[40010],tot,nxt[30100000],dis[19][40010],cnt,f[(1<<17)+105],pos[(1<<17)+105];
vector<int>ve;
bool vis[40010];
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int y)
{
    h[++tot].to=y;
    h[tot].nxt=nxt[x];
    nxt[x]=tot;
}
int read()
{
    int aa=0,bb=1;char cc=getchar();
    while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
    while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^48);cc=getchar();}
    return aa*bb;
}
void dj(int p)
{
    memset(vis,0,sizeof(vis));
    queue<int> q;
    dis[p][ve[p]]=0;
    vis[ve[p]]=1;
    q.push(ve[p]);
    while(q.size()){
        int x=q.front();q.pop();vis[x]=0;
        for(int i=nxt[x];i;i=h[i].nxt){
            int y=h[i].to;
            if(dis[p][y]>=dis[p][x]+1){
                dis[p][y]=dis[p][x]+1;
                if(!vis[y]){
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
}
int main()
{
    memset(dis,0x3f,sizeof(dis));
    memset(f,0x3f,sizeof(f));
    n=read();k=read();m=read();
    int cc;
    for(int i=1;i<=k;i++){
        cc=read();
        a[cc]=1;
    }
    for(int i=1;i<=m;i++) b[i]=read();
    for(int i=0;i<=n;i++){
        c[i]=a[i]^a[i+1];
        if(c[i]) ve.push_back(i);
    }
    for(int i=0;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(i-b[j]>=0) add(i,i-b[j]);
            if(i+b[j]<=n) add(i,i+b[j]);
        }
    }
    cnt=ve.size();
    for(int i=0;i<=cnt;i++) pos[1<<i]=i;
    for(int i=0;i<cnt;i++) dj(i);
    cnt=(1<<cnt)-1;
    f[cnt]=0;
    for(int i=cnt;i>=0;i--){
        for(int j=i;j;j-=lowbit(j)){
            int x=lowbit(j);
            for(int l=j-x;l;l-=lowbit(l)){
                int y=lowbit(l);
                f[i-x-y]=min(f[i-x-y],f[i]+dis[pos[x]][ve[pos[y]]]);
            //    cout<<i-x-y<<" "<<pos[x]<<" "<<pos[y]<<" "<<dis[pos[y]]<<" "<<f[i]<<" "<<f[i-x-y]<<endl;
            }
        }
    }
    printf("%d\n",f[0]);
    return 0;
}
星空

 

出题人好像很喜欢五月天的样子。。。

转载于:https://www.cnblogs.com/jrf123/p/11336796.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值