[CEOI2020]花式围栏
题意
在所给的围栏中算出所含矩形的个数。
solution:
对于计算一个长为
l
l
l,宽为
r
r
r的矩形,可以算出方案为:
(
l
+
1
2
)
∗
(
r
+
1
2
)
{l+1 \choose 2}*{r+1 \choose 2}
(2l+1)∗(2r+1)
考虑将围栏分割成若干个规则的矩形,分别计算贡献并相加。
如图,可以将围栏分成若干个由红色矩形组成的区域,相加即可。(注意,这里是求左端点在给定矩形内时的矩形数,而且必须是极大矩阵,即左右都不能扩展)
如右图,第一个的方案为:
(
l
+
1
2
)
∗
(
r
+
1
2
)
−
(
l
+
1
2
)
∗
(
k
+
1
2
)
{l+1 \choose 2}*{r+1 \choose 2}-{l+1 \choose 2}*{k+1 \choose 2}
(2l+1)∗(2r+1)−(2l+1)∗(2k+1)
这启示我们用单调队列来维护一个高度递增的序列。
若大于当前高度,则如队列;否则将高度大于的弹出,高度差为 m i n ( h [ t o p ] − h [ t o p − 1 ] , h [ t o p ] − h [ i ] ) min(h[top]-h[top-1],h[top]-h[i]) min(h[top]−h[top−1],h[top]−h[i])。
注意,对于高度相等的部分应该合并,而不是单独计算答案。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+5;
const LL mod=1e9+7;
LL n,a[N],b[N],ans;
LL tp,h[N],w[N];
void read(LL &x) {
LL f=1;x=0;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
x*=f;
}
LL calc(LL x, LL y)
{
x %= mod; y %= mod;
x = ((1 + x) * x % mod) * 500000004 % mod;//500000004是2关于膜1e9+7的乘法逆元
y = ((1 + y) * y % mod) * 500000004 % mod;
return x * y % mod;
}
int main() {
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) read(b[i]);
for(int i=1;i<=n;i++) {
LL tot=0;
while(tp>0&&h[tp]>a[i]) {
tot+=w[tp];
ans=(ans+calc(h[tp],tot)-calc(max(h[tp-1],a[i]),tot)+mod)%mod;
b[i]+=w[tp];
tp--;
}
h[++tp]=a[i],w[tp]=b[i];
}
LL tot=0;
for(int i=tp;i>=1;i--) {
tot+=w[i];
ans=(ans+calc(h[i],tot)-calc(h[i-1],tot)+mod)%mod;
}
printf("%lld\n",ans);
}
「NOIP2017普及组」跳房子
solution:
d
p
dp
dp+二分的做法不难想到。
本题显然限制了上界和下界。也就是说,距离太小的不能进,距离太大的要舍弃。而距离是单调递增的,所以用 c n t cnt cnt记录一下位置,把符合条件的加入队列即可。不过本题不是每遍历了一个点就加入一个点,而是在移动 c n t cnt cnt指针时加入。
#include<cstdio>
#include<stack>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int MAXN=500005;
struct node{
ll x,w;
}a[MAXN];
ll n,d,k,sum,dp[MAXN];
bool check(ll mid) {
deque<ll> q;
ll k1=max(1ll,d-mid),k2=d+mid;
ll cnt=0;
for(int i=1;i<=n;i++) {
for(;cnt<i&&a[i].x-a[cnt].x>=k1;cnt++) {
if(!q.size()) q.push_back(cnt);
else {
while(q.size()&&dp[q.back()]<=dp[cnt]) q.pop_back();
q.push_back(cnt);
}
}
while(q.size()&&a[i].x-a[q.front()].x>k2) q.pop_front();
if(q.size()) dp[i]=dp[q.front()]+a[i].w;
else dp[i]=-1e18;
if(dp[i]>=k) return 1;
}
return 0;
}
int main() {
scanf("%lld%lld%lld",&n,&d,&k);
for(int i=1;i<=n;i++) {
scanf("%lld%lld",&a[i].x,&a[i].w);
if(a[i].w>0) sum+=a[i].w;
}
if(sum<k) {
printf("-1");
return 0;
}
ll l=1,r=max(d-1,a[n].x-d);
while(l<r) {
ll mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%lld",l);
}