SZOJ 134 圆环炸裂

题目描述
现在有一个圈,圈上有n个点,每个点之间的距离是m,你从起点出发,带着t个跳跃模组进行巡逻,每次使用一次跳跃模组i,跳跃的距离是ai
一个合法的巡逻路线理应从一个点开始,经过若干次跳跃之后(顺时针或者逆时针),在某个点停下来(不一定要用掉所有的跳跃模组)

或者更正式地说∑ai∗xi≡0(mod m), 其中xi可以是±1,0, 但是不能全为0
一个邪恶的破坏者想要让你无法完成合法的跳跃,现在,你想要知道有多少种可能的破坏(也就是拿掉若干个跳跃模组)方案,答案也许很大,请将答案对10^9+7取余

输入格式
第一行输入三个整数n, m, t
第二行t个整数,表示每个跳跃模组的跳跃距离ai
输出格式
输出使你无法进行巡逻的破坏方案总数

样例输入
7 6 5
5 4 12 6 5
样例输出
6
数据规模
对于10%的数据,t≤10
对于30%的数据,m≤70
对于60%的数据,m≤100
对于100%的数据,2≤n≤1000,1≤m≤120,1≤t≤10000,每个跳跃的距离不超过10^9

一道原本坐等有人接单然后听题解结果石沉大海的题目,只好麻烦把学长问到吐血了

其实题目就是 ∑ai∗xi≡0(mod m), 其中xi可以是±1,0, 但是不能全为0,让其不成立,如果我们真的去破坏的话。。。我也不知道怎么做了,暴力吧

所以我们反过来直接枚举对于 “∑ai∗xi≡0(mod m), 其中xi可以是±1,0, 但是不能全为0” 来说非法的情况

由于是∑ai∗xi≡0(mod m),在mod m的意义下,我们将ai做如下处理,ai=min(ai%m,(m-ai)%m),即在顺时针和逆时针走中取一个最小值,为什么呢,因为我们要压ai的范围,经过这么处理以后,ai的范围就在60以内了

然后我们进行拼凑,拿着目前的ai,只要它mod m 不为0,那么都是对答案有贡献的,将手上所有的ai都用上(即为破坏原题目的合法性)。

这题ai的范围有60,所以我们直接枚举ai就好了,对于目前的长度x,其对应有一个计数器S,二进制下S的每一位表示此时长度是否已经被凑出来过,用0与1表示,那么对于每个长度,首先只要这个长度是合法的,我们就可以将他们都用上,这有一种贡献,当这个长度是我们处理过的ai时,首先我们先确定它是否合法,即看S中有没有已经凑出过的长度,如果有,那么顺时针一下逆时针一下就不能破坏了,这种就对答案没有贡献了,所以在保证是合法的情况下,我们对于每个原有的长度加上ai或者减去ai,咋办呢?——循环位移,左移移右移移就好了,啊,什么是循环位移

所以这道题就是这样啦。。。

其实代码也不会很长。。。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ull unsigned long long
#define mod 1000000007LL
using namespace std;
int n,m,t,cnt[71],REV[1<<16];
ull fullnum;
inline int fix(int x){
    while(x<=0)
        x+=m;
    while(x>=m)
        x-=m;
    return x;
}
inline int rev16bit(int x){
    int ret=0;
    for(int i=1;i<=16;i++)
        ret=2*ret+(x&1),x/=2;
    return ret;
}
inline ull reverse(ull  x)
{
    ull A = (x>>48) & ((1LL << 16)-1);
    ull B = (x>>32) & ((1LL << 16)-1);
    ull C = (x>>16) & ((1LL << 16)-1);
    ull D = (x) & ((1LL << 16)-1);
    A = REV[A], B = REV[B], C = REV[C], D = REV[D];
    return ((D<<48)|(C<<32)|(B<<16)|A);
}
inline ull xh(ull curMask, int shift)
{
    ull ret = curMask;
    ull rev = reverse(curMask) >> (63 - m/2);
    ret |= (curMask << shift);
    ret |= (curMask >> shift);
    ret |= (rev >> (m/2 - shift));
    ret |= (rev << (m/2 + (m&1) - shift));
    return ret & fullnum;
}
inline long long dfs(int x,long long S){
    if(x>m/2+1)
        return 1;
    long long ret=0;
    ret+=dfs(x+1,S);
    if(cnt[x]>0)
        if((S&(1LL<<x))==0)
            ret+=cnt[x]*dfs(x+1,xh(S,x));
    ret=ret%mod;
    return ret;
}
inline void Jimmy(){
    scanf("%d%d%d",&n,&m,&t);
    for(int i=0;i<(1<<16);i++)
        REV[i]=rev16bit(i);
    fullnum=(1LL<<(1+m/2))-1;
    for(int i=1;i<=t;i++){
        long long c;
        scanf("%lld",&c);
        cnt[min(fix(c%m),fix(-(c%m)))]++;
    }
    printf("%lld
",dfs(1,1)%mod);
}
int main(){
    Jimmy();
    return 0;
} 
View Code
原文地址:https://www.cnblogs.com/JimmyC/p/6528673.html