思路:
二分:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
#define fi first
#define se second
#define lson p << 1
#define rson p << 1 | 1
const int maxn = 1e6 + 5, inf = 1e18, maxm = 4e4 + 5, base = 397;
const int mod = 1e9 + 7;
// const int mod = 998244353;
// const __int128 mod = 212370440130137957LL;
const int N = 1e5;
// int a[1005][1005];
int a[maxn], b[maxn];
// bool vis[maxn];
int n, m;
string s;
struct Node{
// int val, id;
// bool operator<(const Node &u)const{
// return val < u.val;
// }
int a, b;
bool operator<(const Node &u)const{
return b < u.b;
}
}c[maxn];
int pw[3005];
void solve(){
int res = 0;
int k, q;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
pw[0] = 1;
for(int i = 1; i <= n; i++){
pw[i] = pw[i - 1] * 2 % mod;
}
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
int dis = a[j] - a[i];
int x = lower_bound(a + 1, a + n + 1, a[i] - dis) - a;
int y = lower_bound(a + 1, a + n + 1, a[j] + dis) - a;
int cnt = n - y + 1 + x - 1;
res = (res + pw[cnt]) % mod;
}
}
cout << res << '\n';
/*
4
0 2 3 5
当枚举到i = 2,j = 3时,相遇的点是2.5,
当枚举到i = 1,j = 4时,因为(i, j)之间不能有点,所以不会重复计算2.5这个点的相同集合
*/
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
动态规划:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
#define fi first
#define se second
#define lson p << 1
#define rson p << 1 | 1
const int maxn = 1e6 + 5, inf = 1e18, maxm = 4e4 + 5, base = 397;
const int mod = 1e9 + 7;
// const int mod = 998244353;
// const __int128 mod = 212370440130137957LL;
const int N = 1e5;
// int a[1005][1005];
int a[maxn], b[maxn];
// bool vis[maxn];
int n, m;
string s;
struct Node{
// int val, id;
// bool operator<(const Node &u)const{
// return val < u.val;
// }
int a, b;
bool operator<(const Node &u)const{
return b < u.b;
}
}c[maxn];
// int ans[maxn], pre[maxn];
int f[3005][3005][2];//f[i][j][k]为前i个,选了第i个,上一个选了第j个,k:0表示第j个往左走,1表示第j个往右走 的不同坐标个数的总和
int pre[3005][3005][2];//f前缀和
int g[3005][3005][2];//g[i][j][k]为前i个,选了第i个,上一个选了第j个,k:0表示第j个往左走,1表示第j个往右走 的不同方案数
int pre2[3005][3005][2];//g前缀和
void solve(){
int res = 0;
int k, q;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
if(n == 2){
cout << 1 << '\n';
return;
}
for(int i = 2; i <= n; i++){
for(int j = 1; j < i; j++){
if(j == 1){
f[i][j][1] = 1;
g[i][j][1] = 1;
}
else{
int dis = a[i] - a[j];
int sum = a[j];
int k = lower_bound(a + 1, a + j, a[j] - dis) - a;
if(k < j){
f[i][j][0] = (pre[j][j - 1][0] - pre[j][k - 1][0] + mod + pre[j][j - 1][1] - pre[j][k - 1][1] + mod) % mod;
g[i][j][0] = (pre2[j][j - 1][0] - pre2[j][k - 1][0] + mod + pre2[j][j - 1][1] - pre2[j][k - 1][1] + mod) % mod;
}
f[i][j][1] = (pre[j][k - 1][0] + pre2[j][k - 1][0] + 1 + pre[j][k - 1][1]) % mod;//加一是因为只选第j个和第i个会贡献一个不同坐标
//加pre2[j][k - 1][0]:设选了k,j,i,k < j < i, 当且仅当从f[j][k][0]转移到f[i][j][1],答案会额外增加加,增加g的某个前缀和
g[i][j][1] = (pre2[j][k - 1][0] + 1 + pre2[j][k - 1][1]) % mod;//加一是因为只选第j个和第i个也是一种选法
}
for(int k = 0; k < 2; k++){
pre[i][j][k] = (pre[i][j - 1][k] + f[i][j][k]) % mod;
pre2[i][j][k] = (pre2[i][j - 1][k] + g[i][j][k]) % mod;
res = (res + f[i][j][k]) % mod;
}
}
}
cout << res << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}