题目大意
给定一条单向链,上面有一些和链方向一致的边,每条边(包括加的)长度都是1,求有多少种方式加边可以使每一对 (i,j) 满足 i<j 有: dis(i,j)=j−i 或 dis(i,j)=j−i−k 。
解答
由题目易知,所有边的长度必须是
k+1
的并且所有边的起点必须在一个
k+1
的连续区间中,所以,所有不满足上述条件的直接输出0,当
k+1>=n
时,直接输出1(没有可以连的边)。
如果并没有在链上的边,我们就可以在每一个长度为
k+1
的区间中随意连线
如果有边,就相当于确定了区间的范围
我们只需要找到第一个可以连边的区间,这个区间的方案数应为
2k+1−边的数量
个,然后我们向后移1个,我们发现,最后一个不连边的方案,显然是包含在上一个区间中的,所以最后一个必须是连边的,故这个区间新增的方案数为
2k−边的数量
个,由此即可得到答案
参考代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1000000007;
int n, m, k;
vector<int> ser;
int tw[1000005];
int l, r;
int main()
{
tw[0] = 1;
for (int i = 1; i < 1000005; i++)
tw[i] = (tw[i-1] + tw[i-1]) % MOD;
ios::sync_with_stdio(false);
cin >> n >> m >> k;
while (m--) {
cin >> l >> r;
if (r-l != 1 && r-l != k+1) {
cout << "0";
return 0;
}
if (r-l == k+1) {
ser.push_back(r);
}
}
k++;
if (ser.size() >= 2) {
if (ser[ser.size()-1] - ser[0] >= k) {
cout << "0";
return 0;
}
}
if (k >= n) {
cout << "1";
return 0;
}
int m = ser.size();
if (m == 0) {
int ans = tw[min(n-k, k)];
for (int i = k+k+1; i <= n; i++) {
ans += tw[k-1];
ans %= MOD;
}
cout << ans;
return 0;
}
int ans = tw[min(n-k, k) - m];
for (int i = max(ser[m-1], 2*k) + 1; i <= n && i-ser[0] < k; i++) {
ans += tw[k-1-m];
ans %= MOD;
}
cout << ans;
return 0;
}