UOJ #141. 【UER #4】量子态的棋盘

12 篇文章 0 订阅

先考虑假设知道了棋盘长什么样,怎么计算每个篮子会接到多少个球

对于一个格子 (i,j) ( i , j ) ,若我们知道会有 x x 个球滚到这个格子,那么一定会有x2+xmod2个球走到这个格子指的方向, x2 ⌊ x 2 ⌋ 个球走到另一个方向
换句话说,只有 xmod2 x mod 2 个球和这个格子的方向有关,其他的球预先已经知道了要走到哪里
当我们知道了棋盘的样子,就可以递推得到每个篮子接到的球数

当我们不知道棋盘长什么样的时候,因为有一些球的走向是和棋盘无关的,我们可以先在棋盘上做一个递推,把不确定方向的球留在棋盘上,得到一个01棋盘,由此可以发现虽然询问的 L,R L , R 很大,但其实有值的答案区间不超过nm

然后对这个棋盘做一个很暴力的轮廓线DP,状态为经过每一条轮廓线从已知区域到未知区域的球数和当前篮子已经接住的球数,走每条轮廓线出来的球不可能>50,可以用一个51进制的数存,写个hash表,大力DP,因为出题人实测状态数很少,这样做跑的过….

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 20;
const int B = 51;
const int hm = 233333;
const int mod = 998244353;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}
inline void dec(int &a,const int &b){a-=b;if(a<0)a+=mod;}

struct Hash_Table
{
    int cnt;
    int nex[hm<<2],ci[hm<<2],cs[hm<<2]; ll cy[hm<<2];
    void init()
    {
        cnt=hm-1;
        memset(nex,-1,sizeof nex);
    }
    void clear()
    {
        while(cnt>hm)
        {
            nex[cnt]=-1,ci[cnt]=cs[cnt]=cy[cnt]=-1;
            cnt--;
        }
        for(int i=0;i<hm;i++) nex[i]=-1,ci[i]=cs[i]=cy[i]=-1;
    }
    void ins(int x,ll y,int i,int s)
    {
        if(ci[x]==-1) { ci[x]=i,cs[x]=s,cy[x]=y; return; }
        int la;
        for(;x!=-1;la=x,x=nex[x]);
        nex[la]=x=++cnt;
        ci[x]=i,cs[x]=s,cy[x]=y;
    }
    int find_(int x,ll y,int s)
    {
        for(;x!=-1;x=nex[x]) if(cy[x]==y&&s==cs[x])
            return ci[x];
        return -1;
    }
}h;

ll pwb[maxn];
struct node
{
    ll x; int s;
    int num;
}f[2][510000]; int len[2],now;

int n,m; ll K;
int rel[maxn],rec[maxn];
char str[maxn];
int w[maxn][maxn]; ll fn[maxn][maxn];
int ans[maxn*maxn],ansu; ll base;

void Solve()
{
    fn[1][1]=K; ansu=w[1][1]=fn[1][1]&1ll;
    for(int i=1;i<=n;i++) for(int j=(i==1?2:1);j<=m;j++)
    {
        fn[i][j]=fn[i][j-1]/2ll+fn[i-1][j]/2ll;
        w[i][j]=fn[i][j]&1ll;
        ansu+=w[i][j];
    }
    for(int i=1;i<=n;i++) if(rel[i]) base+=fn[i][m]/2ll;
    for(int i=1;i<=m;i++) if(rec[i]) base+=fn[n][i]/2ll;

    pwb[0]=1ll;
    for(int i=1;i<=11;i++) pwb[i]=pwb[i-1]*(ll)B;

    h.init();
    int now=0; len[now]=1; f[now][1]=(node){0,0,1};
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            len[now=!now]=0; h.clear();

            for(int k=1;k<=len[!now];k++)
            {
                ll x=f[!now][k].x; int s=f[!now][k].s,temp=f[!now][k].num;
                int c1=x/pwb[j-1]%B,c2=x/pwb[m]%B,cw=c1+c2+w[i][j];
                int cw1=cw/2+(cw&1),cw0=cw/2;

                ll nx; int ns,ni;

                nx=x+pwb[j-1]*(cw0-c1)+pwb[m]*((j==m?0:cw1)-c2); ns=s+cw0*(i==n&&rec[j])+cw1*(j==m&&rel[i]);
                ni=h.find_(nx%hm,nx,ns); 
                if(ni==-1)
                {
                    ni=++len[now]; f[now][ni].x=nx,f[now][ni].s=ns,f[now][ni].num=0;
                    h.ins(nx%hm,nx,ni,ns);
                }
                add(f[now][ni].num,temp);

                nx=x+pwb[j-1]*(cw1-c1)+pwb[m]*((j==m?0:cw0)-c2); ns=s+cw1*(i==n&&rec[j])+cw0*(j==m&&rel[i]);
                ni=h.find_(nx%hm,nx,ns); 
                if(ni==-1)
                {
                    ni=++len[now]; f[now][ni].x=nx,f[now][ni].s=ns,f[now][ni].num=0;
                    h.ins(nx%hm,nx,ni,ns);
                }
                add(f[now][ni].num,temp);
            }
        }
    }
    for(int i=1;i<=len[now];i++)
        add(ans[f[now][i].s],f[now][i].num);
    for(int i=1;i<=ansu;i++) add(ans[i],ans[i-1]);
}

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    scanf("%d%d%lld",&n,&m,&K);
    scanf("%s",str); for(int i=0;i<n;i++) rel[i+1]=str[i]-'0';
    scanf("%s",str); for(int i=0;i<m;i++) rec[i+1]=str[i]-'0';

    Solve();

    int q;scanf("%d",&q);
    while(q--)
    {
        ll l,r; scanf("%lld%lld",&l,&r);
        l-=base; r-=base;
        if(l>ansu||r<0) puts("0");
        else
        {
            r=min(r,(ll)ansu);
            int re=ans[r]; if(l>0) dec(re,ans[l-1]);
            printf("%d\n",re);
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值