D. Sand Fortress
题意:找长度为k的序列,使得序列之和大于等于n,且|A(i)-A(i-1)| <= 1, A0 <= H,求最小的k。
由于k与sum成单增关系,可以二分k的值来找最小的符合条件的k,check采用等差数列求和。
注意要先确定上界避免数据过大
由于mid = l + (r - l)/2 = r - (r-l)/2 属于 (l, r)
为了避免无限循环check之后r != mid + 1, l != mid-1即可
本题中我选取的区间是[l, r], 找符合条件的最小值,l初始值为1
#include
using namespace std;
const int maxn = 100005;
typedef long long ll;
ll n, H;
ll area(ll l, ll r) { return (l + r)*(abs(r - l) + 1)/2; }
bool check(ll mid) {
ll s = 0;
if(mid <= H) {
s = area(1, mid);
}
else {
mid += H - 1;
if(mid & 1) s = area(H, mid/2 + 1) + area(mid/2, 1);
else s = area(H, mid/2) + area(mid/2, 1);
}
return s >= n;
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
while(cin >> n >> H) {
ll l = 1, r = 2*sqrt(n) + 1, mid;
while(r != l) {
mid = l + (r - l)/2;
//printf("l = %lld, r = %lld, mid = %lld\n", l, r, mid);
if(check(mid)) r = mid;
else l = mid + 1;
}
//printf("l = %lld, r = %lld, mid = %lld\n", l, r, mid);
cout << r << endl;
}
return 0;
}
E. Pencils and Boxes
题意:对n个数进行划分,每个区间至少k个数,区间内,|ai-aj| <= d,判定是否存在这种划分。
朴素做法:sort后,令dp(i) = 1代表区间[1, i]上有解。进行O(n^2)dp。
dp[k-1] = a[k-1] - a[1] <= d;
for(int i = k; i <= n; ++i) {
dp[i] = 0;
for(int j = k - 1; j < i; ++j) {
if(i - j >= k) { //[j + 1, i]作为新区间
dp[i] |= a[i] - a[j+1] <= d && dp[j];
} //[1, i]作为一个区间
dp[i] |= a[i] - a[1] <= d;
}
//令dp(0) = 1, 合并两种情况
}
dp[0] = 1;
for(int i = 1; i <= n; ++i) {
dp[i] = 0;
for(int j = 0; j < i; ++j) {
dp[i] |= i - j >= k && a[i] - a[j+1] <= d && dp[j];
}
}
优化:在满足前两个条件的区间内查询是否有dp(j) = 1即可
树状数组维护
#include
#define lowbit(x) x&(-x)
using namespace std;
const int maxn = 500005;
int a[maxn];
bool dp[maxn];
int bit[maxn];
int n, k, d;
void add(int i) {
i++;
while(i <= n + 1) {
bit[i]++;
i += lowbit(i);
}
}
int sum(int i) {
i++;
int ret = 0;
while(i > 0) {
ret += bit[i];
i -= lowbit(i);
}
return ret;
}
bool query(int l, int r) {
if(l > r) return false;
return sum(r) - sum(l-1);
}
int main() {
while(cin >> n >> k >> d) {
for(int i = 1; i <= n; ++i) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
//for(int i = 1; i <= n; ++i) printf("%d\n", a[i]);
dp[0] = 1;
add(0);
int j = 0;
for(int i = 1; i <= n; ++i) {
while(j < i && a[i] - a[j + 1] > d) ++j;
int l = j, r = i - k;
//printf("l = %d, r = %d\n", l, r);
dp[i] = query(l, r);
if(dp[i]) add(i);
//printf("dp[%d] = %d\n", i, dp[i]);
}
puts(dp[n] ? "YES" : "NO");
}
return 0;
}
单调队列维护,q中放dp(i) = 1的i
#include
using namespace std;
const int maxn = 500005;
int a[maxn], q[maxn];
int n, k, d;
int main() {
while(cin >> n >> k >> d) {
for(int i = 1; i <= n; ++i) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
int l = 0, r = 1;
q[l] = 0;
for(int i = k; i <= n; ++i) {
while(l < r && a[i] - a[q[l] + 1] > d) l++;
if(l < r && i - q[l] >= k) q[r++] = i;
}
puts(q[r - 1] == n ? "YES" : "NO");
}
return 0;
}