zoukankan      html  css  js  c++  java
  • dtoi2677「SDOI2016」储能表

    题意:

         有一个 $ n $ 行 $ m $ 列的表格,行从 $ 0 $ 到 $ n - 1 $ 编号,列从 $ 0 $ 到 $ m - 1 $ 编号。

         每个格子都储存着能量。最初,第 $ i $ 行第 $ j $ 列的格子储存着 $ (i mathbin{ ext{xor}} j) $ 点能量。所以,整个表格储存的总能量是

    $$ sumlimits_{i = 0} ^ {n - 1} sumlimits_{j = 0} ^ {m - 1} (i mathbin{ ext{xor}} j) $$

         随着时间的推移,格子中的能量会渐渐减少。一个时间单位,每个格子中的能量都会减少 $ 1 $。显然,一个格子的能量减少到 $ 0 $ 之后就不会再减少了。

         也就是说,$ k $ 个时间单位后,整个表格储存的总能量是

    $$ sumlimits_{i = 0} ^ {n - 1} sumlimits_{j = 0} ^ {m - 1} max((i mathbin{ ext{xor}} j) - k, 0) $$

         给出一个表格,求 $ k $ 个时间单位后它储存的总能量。

         由于总能量可能较大,输出时对 $ p $ 取模,多组数据。

         $ T = 5000 $,$ n leq 10 ^ {18} $,$ m leq 10 ^ {18} $,$ k leq 10 ^ {18} $,$ p leq 10 ^ 9 $

    题解:

         先将问题稍加转化,只需要求有多少个 $(i,j)$ 满足异或值大于 $k$,以及这些位置的异或值之和即可。

         然后发现数据范围非常大,考虑数位dp。$f[i][0/1][0/1][0/1]和g[i][0/1][0/1][0/1]$分别表示前 $i$ 位,行坐标小于还是等于 $n$,列坐标小于还是等于 $m$,当前的异或的值等于还是大于 $k$ 的总和以及方案数。分别枚举两个数字的下一位是 $0$ 还是 $1$ 即可转移。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    int T,p,f[62][2][2][2],g[62][2][2][2];
    long long n,m,K;
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%lld%lld%lld%d",&n,&m,&K,&p);
            memset(f,0,sizeof(f));memset(g,0,sizeof(g));
            f[61][1][1][0]=0;g[61][1][1][0]=1;
            for (int i=61;i>=1;i--)
            for (int j=0;j<=1;j++)
            for (int k=0;k<=1;k++)
            for (int l=0;l<=1;l++)
            for (int a=0;a<=1;a++)
            for (int b=0;b<=1;b++)
            {
                int c,d,e;
                if ((1ll<<i-1)&n)
                {
                    if (!a)c=0;else c=j;
                }
                else
                {
                    if (j && a)continue;else c=j;
                }
                if ((1ll<<i-1)&m)
                {
                    if (!b)d=0;else d=k;
                }
                else
                {
                    if (k && b)continue;else d=k;
                }
                if ((1ll<<i-1)&K)
                {
                    if (!l && !(a^b))continue;else e=l;
                }
                else
                {
                    if (a^b)e=1;else e=l;
                }
                f[i-1][c][d][e]=((long long)f[i-1][c][d][e]+f[i][j][k][l]+g[i][j][k][l]*(a^b)*((1ll<<i-1)%p))%p;
                g[i-1][c][d][e]=(g[i-1][c][d][e]+g[i][j][k][l])%p;
            }
            printf("%lld
    ",((f[0][0][0][1]-(long long)g[0][0][0][1]*(K%p)%p)%p+p)%p);
        }
        return 0;
    }
  • 相关阅读:
    Java学习开篇
    《我的姐姐》
    世上本无事,庸人自扰之
    这48小时
    补觉
    淡定
    es java api 设置index mapping 报错 mapping source must be pairs of fieldnames and properties definition.
    java mongodb groupby分组查询
    linux 常用命令
    mongodb too many users are authenticated
  • 原文地址:https://www.cnblogs.com/1124828077ccj/p/14406928.html
Copyright © 2011-2022 走看看