zoukankan      html  css  js  c++  java
  • NOIP模拟测试17「入阵曲·将军令·星空」

    入阵曲

    题解

    应用了一种美妙移项思想,

    我们先考虑在一维上的做法

    维护前缀和$(sum[r]-sum[l-1])\%k==0$可以转化为

    $sum[r]\% k==sum[l-1]\%k$开个桶维护一下即可

    然后拓展到二维上

    把两行之间所有行拍扁看作一维上的区间,

    我们枚举两行和行之间所有列开个桶维护

    $n^2 m$复杂度

        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=i;j++){
                flag[0]=1;
                for(ll q=1;q<=m;q++){
                    t[q]=(sum[i][q]-sum[j-1][q]+k)%k;
                    cnt+=flag[t[q]];
                    flag[t[q]]++;
                }
                for(ll q=1;q<=m;q++)
                    flag[t[q]]=0;
            }

    至于桶里flag[0]=1初始化含义

    当你其他%==0的矩阵放进去时本身就符合条件,在桶里找配对之前就构成合法矩阵,这样做统计了所有%=0的情况

    当然这样也行

        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=i;j++){
                for(ll q=1;q<=m;q++){
                    t[q]=(sum[i][q]-sum[j-1][q]+k)%k;
                    cnt+=flag[t[q]];
                    flag[t[q]]++;
                }
                cnt+=flag[0];
                for(ll q=1;q<=m;q++)
                    flag[t[q]]=0;
            }

    将军令

    题解

    $45\%$算法

    计算k==1

    和小胖收皇宫类似,

    思考dp数组含义

    $f[x][0]$表示被父亲管辖   $f[x][1]$表示被自己管辖   $f[x][2]$表示被自己儿子管辖

    f数组转移

    若x被父亲管辖,那么儿子必须被自己管辖或者被儿子的儿子管辖

    $f[x][0]=sumlimits_{y}^{yin son}min(f[y][1],f[y][2])$

    若x被自己管辖,那么x转移随意

    $f[x][1]=sumlimits_{y}^{yin son} min(f[y][1],f[y][2],f[y][0])$

    若x被儿子管辖,那么儿子可以被自己管辖或者被儿子的儿子管辖

    但如果所有儿子的儿子代价都比选自己代价小,我们需要强制选出一个$f[y][1]-f[y][2]$差值最小的更新

    代码稍微体会一下

    void dp(ll x){
        if(!son[x]) {
            f[x][1]=1;
            f[x][2]=0x7ffffff;
            f[x][0]=0;
            return ;
        }
        vis[x]=1;
        ll sum=0,sum2=0,sum3=0,pan=0,mix=0x7ffffff;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(vis[y]) continue;
            dp(y);
            sum+=min(f[y][0],min(f[y][1],f[y][2]));
            sum2+=min(f[y][2],f[y][1]);
            sum3+=min(f[y][1],f[y][2]);
            if(f[y][1]<=f[y][2]) pan=1;
            else 
                mix=min(mix,f[y][1]-f[y][2]);
        }
        f[x][1]=sum+1;
        f[x][0]=sum2;
        if(pan==1) f[x][2]=sum3;
        else f[x][2]=sum3+mix;
    }

    $75\%$算法

    计算k==2

    还是思考dp数组含义

    如果还是像上一个那样定义要写死

    换一种0表示被自己守,1表示被儿子守(至少选了一个儿子),2被孙子守(至少选了一个孙子),3子孙全被覆盖自己没有,4孙子全被覆盖(儿子可以被覆盖可以不被覆盖)自己没有

    还是思考转移

    被自己守x转移还是随意$f[x][0]=sumlimits_{y}^{yin son} min(f[y][0],f[y][1],f[y][2],f[y][3],f[y][4])$

    被儿子守x转移比较复杂

    儿子需要自保,因为儿子可以管辖自己兄弟,所以随意选,所以可以选到$f[y][3]$

    $f[x][1]=sumlimits_{y}^{yin son} min(f[y][0],f[y][1],f[y][2],f[y][3])$

    也和k==1类似,显然我们还是要选出来一个最小差值

    被自己孙子守,那么自己儿子需要自保,$f[y][1],f[y][2],f[y][0]$都满足条件

    子孙全被覆盖自己没有,那么就是儿子孙子全被覆盖,儿子本身也要被覆盖,

    $f[y][0]$  $f[y][1]$   $f[y][2]$          (显然$f[y][3]$不行)

    孙子全被覆盖,那么就是孙子全被覆盖,那么可以是$f[y][3]$,$f[y][1]$,$f[y][0]$,$f[y][2]$

    代码稍微体会一下(这个代码有误,我并不能调出来)

    void dp2(ll x){
    //1表示被自己守,2表示被儿子,3被孙子,4子孙全有自己无,5孙子全有自己无
        if(!son[x]){
            f2[x][4]=0x7fffffff;
            f2[x][5]=0x7fffffff;
            f2[x][1]=1;
            f2[x][2]=0;
            f2[x][3]=0;
            return ;
        }
        vis[x]=1;
        ll sum1=0,sum2=0,sum3=0,sum4=0,sum5=0,pan=0,mix1=0x7fffffff,pan2=0,mix2=0x7ffffff;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(vis[y]) continue;
            dp2(y);
            sum1+=min(min(f2[y][1],f2[y][2]),min(f2[y][3],min(f2[y][4],f2[y][5])));
            sum2+=min(min(f2[y][1],f2[y][2]),min(f2[y][3],f2[y][4]));
            if(f2[y][1]<=f2[y][2]&&f2[y][1]<=f2[y][3]&&f2[y][1]<=f2[y][4])
                pan=1;
            else
                mix1=min(mix1,f2[y][1]-min(f2[y][2],min(f2[y][3],f2[y][4])));
            sum3+=min(f[y][1],min(f[y][2],f[y][3]));
            if(f2[y][2]<=f2[y][1]&&f2[y][2]<=f2[y][3])
                pan2=1;
            else
                mix2=min(mix2,f2[y][2]-min(f2[y][1],f2[y][3]));
            sum4+=min(min(f2[y][1],f2[y][2]),f2[y][3]);
            sum5+=min(min(f2[y][1],f2[y][2]),min(f2[y][3],f2[y][4]));
        }
        f2[x][1]=1+sum1;
        if(pan==1)
            f2[x][2]=sum2;
        else f2[x][2]=sum2+mix1;
        if(pan2==1)
            f2[x][3]=sum3;
        else f2[x][3]=sum3+mix2;
        f2[x][4]=sum4;
        f2[x][5]=sum5;
    }

    思考怎么优化

    dp数组含义再次转变

    $f[x][w]$表示$f$中<=w最小的数

    例如$f[x][4]$表示$min(f[x][1],f[x][2],f[x][3],f[x][4],f[x][0])$

    转移类似

    $f[x][0]=sumlimits_{y}^{yin son} f[y][4]  $$ +1$

    $f[x][3]=sumlimits_{y}^{yin son} f[y][2]  $

    $f[x][2]=sumlimits_{y}^{yin son} f[y][3]  $$+差值$

    $f[x][4]=sumlimits_{y}^{yin son} f[y][3]  $

    $f[x][1]=sumlimits_{y}^{yin son} f[x][4]  $$+差值$

    最后1 2 3 4 互相取min具体看代码

    再次感受一下

    void dp2(ll x){
        if(!son[x]){
            f2[x][0]=1;
            f2[x][1]=f2[x][2]=1;f2[x][3]=f2[x][4]=0;
            f2[x][1]=0x7fffffff;
            return ;
        }
        vis[x]=1;
        f2[x][0]=1;
        f2[x][1]=f2[x][2]=f2[x][3]=f2[x][4]=0;
        ll pan=0,mix1=0x7fffffff,pan2=0,mix2=0x7ffffff;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(vis[y]) continue;
            dp2(y);
            f2[x][0]+=f2[y][4];
            f2[x][3]+=f2[y][2];
            f2[x][4]+=f2[y][3];
            mix1=min(f2[y][0]-f2[y][3],mix1);
            mix2=min(f2[y][1]-f2[y][2],mix2);
        }
        f2[x][1]=f2[x][4]+mix1;
        f2[x][2]=min(f2[x][3]+mix2,min(f2[x][0],f2[x][1]));
        f2[x][3]=min(f2[x][3],f2[x][2]);
        f2[x][4]=min(f2[x][3],f2[x][4]);
    //    printf("mix1=%lld 2=%lld x=%lld f[][1]=%lld [2]=%lld [3]=%lld [4]=%lld [0]=%lld
    ",mix1,mix2,x,f2[x][1],f2[x][2],f2[x][3],f2[x][4],f2[x][0]);
    }

     $100\%$算法

    dp我是打不出来

    应该会有别的大神打出来dp100分

    那么正解是什么神奇的算法呢?

    简单贪心!

    啊啊啊啊啊又是贪心,我又没有看出来它是贪心,awsl,我太菜了

    步骤分比正解难的多得多

    贪心10分钟改完AC,每次找到最深得节点找它的k级父亲

    实现不要想复杂,暴力改,暴力跳即可

    你会发现你比dp还要快!!!!!!!!!!!!!!!!!!!

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define A 1010101
    ll n,k,t,tot=0,sum1=0,sum2=0,sumji=0,sumou=0,skriller=0;
    ll head[A],nxt[A],ver[A],deep[A],son[A],f[A][3],fa[A];
    ll f2[A][10];
    struct node{
        ll deep,x;
        friend bool operator < (node a,node b){
            return a.deep<b.deep;
        }
    }point[A];
    priority_queue<node> q;
    bool vis[A];
    void add(ll x,ll y){
        nxt[++tot]=head[x];
        head[x]=tot;
        ver[tot]=y;
    }
    ll read(){
        ll f=1,x=0;char c=getchar();
        while(!isdigit(c)){
            if(c=='-') f=-1;
            c=getchar();
        }
        while(isdigit(c)){
            x=x*10+c-'0';
            c=getchar();
        }
        return f*x;
    }
    void dfs(ll x,ll de){
        vis[x]=1;
        point[x].deep=de;
        point[x].x=x;
        q.push(point[x]);
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(vis[y]) continue;
            dfs(y,de+1);
            son[x]++;
            fa[y]=x;
        }
    }
    void dp(ll x){
        if(!son[x]) {
            f[x][1]=1;
            f[x][2]=0x7ffffff;
            f[x][0]=0;
            return ;
        }
        vis[x]=1;
        ll sum=0,sum2=0,sum3=0,pan=0,mix=0x7ffffff;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(vis[y]) continue;
            dp(y);
            sum+=min(f[y][0],min(f[y][1],f[y][2]));
            sum2+=min(f[y][2],f[y][1]);
            sum3+=min(f[y][1],f[y][2]);
            if(f[y][1]<=f[y][2]) pan=1;
            else 
                mix=min(mix,f[y][1]-f[y][2]);
        }
        f[x][1]=sum+1;
        f[x][0]=sum2;
        if(pan==1) f[x][2]=sum3;
        else f[x][2]=sum3+mix;
    }
    ll find(ll x){
        ll w=1;
        while(w<=k){
            w++;
            x=fa[x];
        }
        return x;
    }
    void dp2(ll x){
        if(!son[x]){
            f2[x][0]=1;
            f2[x][1]=f2[x][2]=1;f2[x][3]=f2[x][4]=0;
            f2[x][1]=0x7fffffff;
            return ;
        }
        vis[x]=1;
        f2[x][0]=1;
        f2[x][1]=f2[x][2]=f2[x][3]=f2[x][4]=0;
        ll pan=0,mix1=0x7fffffff,pan2=0,mix2=0x7ffffff;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(vis[y]) continue;
            dp2(y);
            f2[x][0]+=f2[y][4];
            f2[x][3]+=f2[y][2];
            f2[x][4]+=f2[y][3];
            mix1=min(f2[y][0]-f2[y][3],mix1);
            mix2=min(f2[y][1]-f2[y][2],mix2);
        }
        f2[x][1]=f2[x][4]+mix1;
        f2[x][2]=min(f2[x][3]+mix2,min(f2[x][0],f2[x][1]));
        f2[x][3]=min(f2[x][3],f2[x][2]);
        f2[x][4]=min(f2[x][3],f2[x][4]);
    //    printf("mix1=%lld 2=%lld x=%lld f[][1]=%lld [2]=%lld [3]=%lld [4]=%lld [0]=%lld
    ",mix1,mix2,x,f2[x][1],f2[x][2],f2[x][3],f2[x][4],f2[x][0]);
    }
    void dfs2(ll x,ll fa,ll de){
        vis[x]=1;
        if(de==k) return ;
        for(ll i=head[x];i;i=nxt[i]){
            ll y=ver[i];
            if(y==fa) continue;
            dfs2(y,x,de+1);
        }
    }
    int main(){
        n=read(),k=read(),t=read();
    //    printf("%lld
    ",k);
        if(k==0){
            for(ll i=1,a,b;i<=n-1;i++){
                a=read(),b=read();
            }
            printf("%lld
    ",n);
            return 0;
        }
    /*    if(k==1){
            for(ll i=1,a,b;i<=n-1;i++){
                a=read(),b=read();
                add(a,b);
                add(b,a);
            }
            dfs(1,1);
            memset(vis,0,sizeof(vis));
            dp(1);
            printf("%lld
    ",min(f[1][1],f[1][0]));
            return 0;
        }
    */    if(k==2){
            memset(f2,0x3f,sizeof(f2));
            for(ll i=1,a,b;i<=n-1;i++){
                a=read(),b=read();
                add(a,b);
                add(b,a);
            }
            dfs(1,1);
            memset(vis,0,sizeof(vis));
            dp2(1);
            printf("%lld
    ",f2[1][2]);
            return 0;
        }
        else{
            for(ll i=1,a,b;i<=n-1;i++){
                a=read(),b=read();
                add(a,b);
                add(b,a);
            }
            dfs(1,1);
            memset(vis,0,sizeof(vis));
            while(!q.empty()){
                ll x=q.top().x;
                q.pop();
                if(vis[x]) continue;
                ll f=find(x);
    //            printf("x=%lld f=%lld
    ",x,f);
                dfs2(f,0,0);
                skriller++;
            }
            printf("%lld
    ",skriller);
        }
    }

    星空

    题解

    首先如果翻转我们楞翻转一次复杂度最高n那么考虑优化

    我们将取反转化为异或

    思考$1 xor 1=0$

    $0 xor 1=1$

    那么取反我们就转化为了$xor 1$

    定义差分数组为$b[i]=a[i] xor a[i+1]$

    整段区间$xor1$差分(第一次看到异或的差分),我们转化为$l xor1$ $r+1xor1$

    我们不可能白白翻转一段全是$1$的

    我们翻转至少有$1$个零

    翻转两端都有$0$那么就可以看作消去

    两端只有一端有$0$那么可以看作移动

    那么问题就转化为了如何最少移动消去使所有$0$变为$1$

    处理出任意两个点之间消去代价(可以完全背包,把每个操作换成$+ $,$-$ 两个代价)

        for(ll i=1;i<=m;i++)
            for(ll j=a[i];j<=n;j++)
                d[j]=min(d[j-a[i]]+1,d[j]);
    
        for(ll i=1;i<=m;i++)
            for(ll j=n-a[i];j;j--)
                d[j]=min(d[j+a[i]]+1,d[j]);

    考虑k很小,然后状压解决把所有点消去代价

        memset(f,0x7f,sizeof(f));
        f[0]=0;
        for(ll i=0;i<ci[cnt];i++){
            for(ll j=0;j<cnt;j++){
                if(!(ci[j]&i)){
                    
                    for(ll k=j+1;k<cnt;k++){
                        if((!(ci[k]&i))){
                            if(f[i]>100000000) continue;
                            else {
    //                            printf(" i=%lld j=%lld k=%lld cij=%lld cik=%lld f=%lld
    ",i,j,k,ci[j],ci[k],f[i]);
                                f[i|ci[j]|ci[k]]=min(f[i|ci[j]|ci[k]],f[i]+d[abs(pos[j]-pos[k])]);
    //                            printf("f=%lld d=%lld
    ",f[i],d[abs(pos[j]-pos[k])]);
                            }
                        }
                    }
                    break;
                }
            }
        }

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define A 111111
    ll b[A],d[A],Xor[A],pos[A],ci[A],f[A],a[A];
    ll cnt,m,n,k;
    inline void init()
    {
        memset(d,0x3f,sizeof(d));
        d[0]=0;
        for(ll i=1;i<=m;i++)
            for(ll j=a[i];j<=n;j++)
                d[j]=min(d[j-a[i]]+1,d[j]);
    
        for(ll i=1;i<=m;i++)
            for(ll j=n-a[i];j;j--)
                d[j]=min(d[j+a[i]]+1,d[j]);
        for(ll i=1;i<=n;i++){
            if(Xor[i])
                pos[cnt++]=i;
        }
    /*    for(ll i=1;i<=n;i++){
            printf("%lld
    ",d[i]);
        }
        printf("cnt=%lld
    ",cnt);
    */}
    inline void dp(){
        memset(f,0x7f,sizeof(f));
        f[0]=0;
        for(ll i=0;i<ci[cnt];i++){
            for(ll j=0;j<cnt;j++){
                if(!(ci[j]&i)){
                    
                    for(ll k=j+1;k<cnt;k++){
                        if((!(ci[k]&i))){
                            if(f[i]>100000000) continue;
                            else {
    //                            printf(" i=%lld j=%lld k=%lld cij=%lld cik=%lld f=%lld
    ",i,j,k,ci[j],ci[k],f[i]);
                                f[i|ci[j]|ci[k]]=min(f[i|ci[j]|ci[k]],f[i]+d[abs(pos[j]-pos[k])]);
    //                            printf("f=%lld d=%lld
    ",f[i],d[abs(pos[j]-pos[k])]);
                            }
                        }
                    }
                    break;
                }
            }
        }
    }
    int main(){
        ci[0]=1;
        for(ll i=1;i<=30;i++)
            ci[i]=ci[i-1]<<1/*,printf("ci[%lld]=%lld
    ",i,ci[i])*/;
        scanf("%lld%lld%lld",&n,&k,&m);
        n++;
        for(ll i=1,ak;i<=k;i++){
            scanf("%lld",&ak);
            b[ak]^=1;
        }
        for(ll i=1;i<=n;i++){
            Xor[i]=b[i-1]^b[i];
        }
        for(ll i=1;i<=m;i++){
            scanf("%lld",&a[i]);
        }
        init();
        dp();
        printf("%lld
    ",f[ci[cnt]-1]);
    }
    我已没有下降的余地
  • 相关阅读:
    【算法】Manacher算法
    python 02 python入门知识
    python 01:计算机基础知识
    表示数值的字符串
    C++ 迭代器(STL迭代器)iterator详解
    构建乘积数组
    C++ 容器(STL容器)
    数组中重复的数字
    把字符串转换成整数
    十大经典排序算法
  • 原文地址:https://www.cnblogs.com/znsbc-13/p/11336022.html
Copyright © 2011-2022 走看看