HGOI 20191106

t1 旅行家(traveller)

2s,256MB

【题目背景】

小X热爱旅行,他梦想有一天可以环游全世界……

【题目描述】

现在小X拥有n种一次性空间转移装置,每种装置可以使他前进ai光年,每种装置他拥有bi个。(小X作为一个旅行家,是不会后退的;他的初始坐标是0)

他突然对宇宙的根源感到十分好奇,他发现他用完所有的装置刚好能够到达,于是她就开始了他的旅行。

邪恶的光明法师小S听说了这件事,他决定阻止可爱的小X,于是他使出了扭转乾坤的神通,在前进道路上的m个节点上创造出了可以吞噬一切的黑洞,它们的坐标分别是ci。

小X当然不希望旅行失败,为了嘲讽小S,他想知道他有多少种不同的方式到达宇宙的根源。但是小X也不想过分麻烦你,所以他只想知道答案对100000007取模以后的结果。

注意:每种装置本质相同,即连续使用多次同种装置,但顺序不同算作1种。

【输入格式】

第一行一个正整数n,表示小X拥有的一次性空间转移装置的数量。

接下来n行每行两个正整数ai,bi,分别表示该装置可以使他前进ai光年,他拥有bi个该装置。

接下来一行一个正整数m,表示小S制造出的黑洞数目。

接下来一行m个正整数ci,表示黑洞所在的坐标。

【输出格式】

一行一个正整数,表示答案对100000007取模以后的结果。

【样例输入输出】

traveller.in

2
2 1
3 1
1
1

traveller.out

2

【样例解释】

1号节点存在黑洞,不能通行。

合法的方式为:先使用装置1,再使用装置2;先使用装置2,再使用装置1。

【数据范围】

对于30%的数据,n≤3,bi≤5,m≤3。

对于另外20%的数据,n≤5,bi≤10,ai≤100。

对于另外10%的数据,m=0。

对于另外10%的数据,ai≤100。

对于100%的数据,n≤6,m≤105,0<ci<∑(bi*ai),bi≤12,ai≤109。

题解

显然,所有能被访问到的点是很少的,并且还要求方案,显然一些状态是废的。 我们可以用状态压缩,用 \(f_{a_1 ... a_n}\) 表示当时每一种推进器用了多少个时的方案。

可以使用set<int>表示不能的到达的点。

转移:\(f_{i,j,k,l,n,m} = f_{i-1,j,k,l,m,n} + f_{i,j-1,k,l,m,n} + f_{i,j,k-1,l,m,n} + f_{i,j,k,l-1,m,n} + f_{i,j,k,l,m-1,n} + f_{i,j,k,l,m,n-1}\)

注意到 \(b_i <= 12\) 最多有 13 种方案, 可以状态压缩。

代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define MOD 100000007
using namespace std;
long long reflex[5010000];
long long f[5010000];
set <int> t;
int n, m;
long long a[22];
long long b[22];
long long c[110000];
long long p[20];
void DFS(int x, long long tot, long long prefix){
    f[prefix] = tot;
    if (t.find(tot) != t.end()) reflex[prefix] = -1; // banned
    //cout << prefix << ' ' << f[prefix] << ' ' << reflex[prefix] << endl;
    if (x == n+1) return;
    for (int i=0;i<=b[x];i++){
        DFS(x+1, tot+i*a[x], prefix + p[x] * i);
    }
}
int main(){
    freopen("traveller.in","r",stdin);
    freopen("traveller.out","w",stdout);

    p[1] = 1;
    for (int i=2;i<=13;i++)
        p[i] = p[i-1] * 13;
    cin >> n;
    int fin = 0;
    for (int i=1;i<=n;i++)
        cin >> a[i] >> b[i], fin += b[i] * p[i];
//  cout << fin << endl;
    cin >> m;
    for (int i=1;i<=m;i++)
        scanf("%lld", c+i), t.insert(c[i]);
    DFS(1, 0, 0);
    reflex[0] = 1;
    for (int i=1;i<=fin;i++){
        if (reflex[i] != -1)
        for (int j=1;j<=n;j++){
            if (i % p[j+1] / p[j] != 0 && reflex[i - p[j]] != -1)
                reflex[i] = (reflex[i] + reflex[i - p[j]]) % MOD;
        }
    }
    cout << reflex[fin] << endl;
}
12-31 21:47