【DP的胡策】互测3.30 (状压dp+dp+找规律)

T1:

这里写图片描述

题解:

不难发现是个状压dp,其实我们只需要维护这四个格子的值进行转移就好了
这里写图片描述
k为状压,值为1的时候代表这个数字还没有取

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int map[2010][2010];
char s[2010];
int h,w;
int f[2010][2010][2][2][2][2];
int main()
{
    int i,j,k,l,ans=0x3f3f3f3f;
    scanf("%d%d",&h,&w);
    for(i=1;i<=h;i++){
        scanf("%s",s+1);
        for(j=1;j<=w;j++)
            if(s[j]!='.')
                map[i][j]=s[j]-'0';
    }
    memset(f,0x3f,sizeof(f));
    if(map[1][2]&&map[2][1]){
        f[1][1][1][0][1][1]=map[1][1]+map[2][1];
        f[1][1][1][1][0][1]=map[1][1]+map[1][2];
    }
    if(!map[1][2]||!map[2][1])
        f[1][1][1][1][1][1]=map[1][1];
    for(i=1;i<=h;i++)
        for(j=1;j<=w;j++)
            for(k=0;k<16;k++)
                if(f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]<0x3f3f3f3f){
                    l=(map[i+1][j+1]!=0)+(map[i+2][j]!=0)+((k&1)&&(map[i+1][j-1]!=0));
                    if(l==3){
                        f[i+1][j][1][0][0][(k>>2)&1]=min(f[i+1][j][1][0][0][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j+1]+map[i+2][j]);
                        f[i+1][j][1][1][0][(k>>2)&1]=min(f[i+1][j][1][1][0][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j+1]+map[i+1][j-1]);
                        f[i+1][j][1][0][1][(k>>2)&1]=min(f[i+1][j][1][0][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+2][j]+map[i+1][j-1]);
                    }
                    if(l==2){
                        if(map[i+1][j-1]&&(k&1))
                            f[i+1][j][1][1][1][(k>>2)&1]=min(f[i+1][j][1][1][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j-1]);
                        if(map[i+2][j])
                            f[i+1][j][1][0][1][(k>>2)&1]=min(f[i+1][j][1][0][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+2][j]);
                        if(map[i+1][j+1])
                            f[i+1][j][1][1][0][(k>>2)&1]=min(f[i+1][j][1][1][0][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j+1]);
                    }
                    if(l<=1)
                        f[i+1][j][1][1][1][(k>>2)&1]=min(f[i+1][j][1][1][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1));
                    l=(map[i+1][j+1]!=0)+(map[i][j+2]!=0)+(((k>>3)&1)&&(map[i-1][j+1]!=0));
                    if(l==3){
                        f[i][j+1][(k>>1)&1][0][0][1]=min(f[i][j+1][(k>>1)&1][0][0][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i+1][j+1]+map[i][j+2]);
                        f[i][j+1][(k>>1)&1][0][1][1]=min(f[i][j+1][(k>>1)&1][0][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i+1][j+1]+map[i-1][j+1]);
                        f[i][j+1][(k>>1)&1][1][0][1]=min(f[i][j+1][(k>>1)&1][1][0][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i][j+2]+map[i-1][j+1]);
                    }
                    if(l==2){
                        if(map[i-1][j+1]&&((k>>3)&1))
                            f[i][j+1][(k>>1)&1][1][1][1]=min(f[i][j+1][(k>>1)&1][1][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i-1][j+1]);
                        if(map[i][j+2])
                            f[i][j+1][(k>>1)&1][1][0][1]=min(f[i][j+1][(k>>1)&1][1][0][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i][j+2]);
                        if(map[i+1][j+1])
                            f[i][j+1][(k>>1)&1][0][1][1]=min(f[i][j+1][(k>>1)&1][0][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i+1][j+1]);
                    }
                    if(l<=1)
                        f[i][j+1][(k>>1)&1][1][1][1]=min(f[i][j+1][(k>>1)&1][1][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1));
                }
    for(k=0;k<16;k++)
        if(ans>f[h][w][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1])
            ans=f[h][w][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1];
    printf("%d\n",ans);
    return 0;
}

T2:

这里写图片描述

题解:

n个珠子用一条线穿起来,手玩一下不难发现是2^n种
那么f[i][j]表示前i个珠子分成j组的方案数,那么 f[i][j]=i1k=1f[k][j1]2ik+1 f [ i ] [ j ] = ∑ k = 1 i − 1 f [ k ] [ j − 1 ] ∗ 2 i − k + 1
然后我们用超群的智慧打出一张表,up上午已经把表删了现在直接粘别人的了
这里写图片描述
联系一下f[i][j]会和f[i-1][j]/f[i-1][j-1]/f[i][j-1]发生奇奇怪怪的关系
我们找到了! f[i][j]=f[i1][j1]+2f[i1][j] f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + 2 ∗ f [ i − 1 ] [ j ]
然后可以O(n^2)预处理+O(1)回答询问可以完美解决这个问题。

代码:

#include<cstdio>
#define LL long long
using namespace std;
const int N=5000;
const int mod=104857601;
LL f[N+5][N+5];
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    f[1][1]=1; f[2][1]=2;
    LL all=4;
    for (int i=3;i<=N;i++) f[i][1]=all,all=(all+f[i][1])%mod;
    for (int i=2;i<=N;i++)
        for (int j=i;j<=N;j++)
            f[j][i]=(f[j-1][i-1]+2LL*f[j-1][i]%mod)%mod;
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,m;scanf("%d%d",&n,&m);    
        if(n==0) {printf("0\n");continue;}
        if(m>n) {printf("0\n");continue;}
        if(m==n) {printf("1\n");continue;}  
        printf("%lld\n",f[n][m]);
    }
}

T3:

这里写图片描述

题解:

为什么我不截数据范围呢?因为这是一道结论规律题啊
规律就是答案是区间内的最大值*2
我们有科学严谨的证明
首先,加入别的数没有我们找到的数的最高位“1”,那么在&上我们可以领先一个最高位,在|上即使后面的全都是1也无法弥补
然后,假如最高位上有“1”,(这个有1只能是全是1,如果有一个0的话可以去看证明第一步。),我们肯定会在&上舍弃一个相对的高位,这样后面全|回来也没用
相等?相等不就是本身*2吗。
证毕
我们可以用RMQ实现O(1)回答询问

代码:

#include <cmath>
#include <cstdio>
#include <iostream>
#define sz 19
#define LL long long
using namespace std;
const int N=36005;
LL maxx[N][sz];int n,m;
void rmq()
{
    for (int j=1;j<sz;j++)
      for (int i=1;i<=n;i++)
        if (i+(1<<j)-1<=n)
          maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<j-1)][j-1]);
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%lld",&maxx[i][0]);
    rmq();LL ans=0;
    while (m--)
    {
        LL x,y;scanf("%lld%lld",&x,&y);
        LL l=(x+ans)%n+1,r=(y+ans)%n+1;
        if (l>r) swap(l,r); ans=0;
        int k=log2(r-l+1);
        ans=2LL*max(maxx[l][k],maxx[r-(1<<k)+1][k]);
        printf("%lld\n",ans);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值