HDU 6129 规律+数论

题意:

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6129
给出一串长度为n的序列a,现在对a求m次前缀异或和,求最后得到的数组。


思路:

题目不长,但其实隐藏了很多知识点。
首先写几行找找规律:
a b c d
a ab abc abcd
a aab aaabbc aaaabbbccd
a aaab aaaaaabbbc aaaaaaaaaabbbbbbcccd
如果只看a的贡献,可以发现a对第x行,第y列的数的贡献系数是C(x+y-2,x-1),再来观察每个位置的a如果系数是xx,那么a之后的一个位置b的系数也是xx,同理c,d等系数都是xx,因此,只要求出来一个位置a的系数,那么对于之后的每个位置关于bcd的系数也都相同。因为这题需要算异或和,所以要知道组合数C(x+y-2,x-1)的奇偶性。
考虑Lucas定理:C(n,m)%p = C(n/p,m/p) * C(n%p,m%p) % p
令p=2,则C(n,m)%2 =C (n/2,m/2) * C(n%2,m%2) % 2
n%2和m%2其实就是n和m的二进制最后一位,很显然,如果C(n,m)是奇数,当m的最后一位为1时,n的最后一位也必须为1,否则C(0,1)=0,那么C(n,m)%2=0,C(n,m)就是偶数了。对于n/2和m/2,其实就是n和m同时二进制右移一位,因此n和m的每一位都会成为最后一位来比较,根据上面得到的,所以m每个为1的二进制位,n在这一位也必须是1,否则C(n,m)就是偶数,这种要求等价于(n&m)==m。这样就可以O(1)判断组合数奇偶性了。


代码:

#include 
using namespace std;
const int MAXN = 2e5 + 10;

int a[MAXN], b[MAXN];

int main() {
    //freopen("in.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        memset(b, 0, sizeof(b));
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            int x = i + m - 2, y = m - 1;
            if ((x & y) == y) {
                for (int j = i; j <= n; j++)
                    b[j] ^= a[j - i + 1], cnt++;
            }
        }
        //cout << cnt << endl;
        for (int i = 1; i <= n; i++)
            printf("%d%c", b[i], i == n ? '\n' : ' ');
    }
    return 0;
}

你可能感兴趣的:(找规律,数学推导)