F - Box in Box
题意:
给定 n n n个长宽高为 w , h , d w, h, d w,h,d的长方体,问是否存在两个长方体 i , j i,j i,j满足 w i < w j , h i < h j , d i < d j w_i<w_j,h_i<h_j,d_i<d_j wi<wj,hi<hj,di<dj。
思路:
因为我们只关心相对大小,所以我们先对其进行离散化,然后按 w w w从小到大排序。枚举到当前长方体时,将离散化后的 h h h作为下标,对应的值为 d d d,那么我们只需查询小于当前 h h h的所以下标对应的值的最小值即可。
代码:
const int N = 6e5 + 50;
const int M = N << 1;
const int mod = 1e9 + 7;
template<typename T>
struct BIT{
const int n;
vector<T> t;
BIT<T> () {}
BIT<T> (int _n): n(_n), t(_n + 1, inf) { }
BIT<T> (int _n, T *a): n(_n) {
memset(t, 0, sizeof(t));
for (int i = 1; i <= n; ++ i){
t[i] += a[i];
int j = i + lowbit(i);
if (j <= n) t[j] += t[i];
}
}
void add(int i, T x){
while (i <= n){
t[i] += x;
i += lowbit(i);
}
}
void add(int i, int j, T x) {
add(i, x); add(j + 1, -x);
}
void updata(int i, int val) {
while(i <= n) {
t[i] = min(t[i], val);
i += lowbit(i);
}
}
T get(int i) {
int res = inf;
while(i) {
res = min(res, t[i]);
i -= lowbit(i);
}
return res;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<pair<int, pii>> a(n);
vector<int> lsh;
lsh.pb(0);
for(int i = 0; i < n; i++) {
int x, y, z;
cin >> x >> y >> z;
vector<int> now = {x, y, z};
sort(all(now));
a[i] = make_pair(now[0], make_pair(now[1], now[2]));
lsh.pb(now[1]), lsh.pb(now[2]);
}
sort(all(lsh));
lsh.erase(unique(all(lsh)), lsh.end());
sort(all(a));
BIT<int> bit(n * 2 + 10);
vector<int> id(n + 1);
int last = 0;
for(int i = 0; i < n; i++) {
id[i] = lower_bound(all(lsh), a[i].se.fi) - lsh.begin();
while(last < i && a[last].fi < a[i].fi) {
bit.updata(id[last], a[last].se.se);
last++;
}
if(bit.get(id[i] - 1) < a[i].se.se) {
cout << "Yes\n";
return 0;
}
}
cout << "No\n";
return 0;
}
G - Ban Permutation
题意:
构造长度为 n ( n ≤ 100 ) n(n\leq100) n(n≤100)的排列,满足 ∣ p i − i ∣ ≥ x ( x ≤ 5 ) |p_i-i| \geq x(x\leq5) ∣pi−i∣≥x(x≤5),输出合法的方案数。
思路:
如果反过来思考求
∣
p
i
−
i
∣
<
x
|p_i-i| < x
∣pi−i∣<x的数量,那么
p
i
p_i
pi的取值最多也就9种。看起来会更好做一点?所以考虑容斥,求出满足至少有一个位置满足
∣
p
i
−
i
∣
<
x
|p_i-i|<x
∣pi−i∣<x的方案数。
我们定义
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]:前i个位置填了
j
j
j个数,
j
j
j个数均满足
∣
p
i
−
i
∣
<
x
|p_i-i|<x
∣pi−i∣<x,且第
i
i
i个位置选的数的状态是
k
k
k的总方案数。
k
k
k是一个长度为
2
∗
x
−
1
2*x-1
2∗x−1的二进制数。
转移很好转移,最后利用容斥转一下就可以了。
代码:
int dp[105][105][600];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, x;
cin >> n >> x;
int lim = 1 << (2 * x - 1);
dp[0][0][0] = 1;
for(int i = 0; i < n; i++) {
for(int j = 0; j <= i; j++) {
for(int k = 0; k < lim; k++) {
if(!dp[i][j][k]) continue;
dp[i + 1][j][k >> 1] = (dp[i + 1][j][k >> 1] + dp[i][j][k]) % mod;
for(int l = i + 1 - x + 1; l <= i + 1 + x - 1; l++) {
if(l >= 1 && l <= n && !((k >> 1) & (1 << (l - i - 2 + x)))) {
dp[i + 1][j + 1][((k >> 1) | (1 << (l - i + x - 2)))] += dp[i][j][k];
dp[i + 1][j + 1][((k >> 1) | (1 << (l - i + x - 2)))] %= mod;
}
}
}
}
}
LL fact[111];
fact[0] = 1;
for(int i = 1; i <= 100; i++) {
fact[i] = fact[i - 1] * i % mod;
}
LL ans = 0;
for(int i = 0; i <= n; i++) {
LL cur = 0;
for(int j = 0; j < lim; j++) {
cur = (cur + dp[n][i][j]) % mod;
}
cur = cur * fact[n - i] % mod;
if(i & 1) ans = (ans - cur + mod) % mod;
else ans = (ans + cur) % mod;
}
cout << ans << '\n';
return 0;
}