cf526F
• n * n的矩形,每行每列有且仅有一个特殊点
• 求问存在多少k * k的子矩形,使得子矩形内有k个特殊点
• 1 ≤ n ≤ 3 * 1e5
• 二维前缀和
• 枚举即可
• O(n ^ 3) 肯定T
• 转换为数列A,每个特殊点的横坐标为下标,纵坐标为权值 • Ax = y
• 题目相当于询问存在多少(l, r)使得
• max(Al…Ar) - min(Al…Ar) == r - l
• O(n ^ 2)
• 考虑分治
• 1. 最大值最小值在同侧 • 通过枚举l可以计算出r,判断是否可行即可
• 2. 最大值最小值在异侧 • 假设最小值在左侧,最大值在右侧 • max(Amid…Ar) - r == min(Al…Amid) - l
• 由于最大值单调不减,最小值单调不增
• min(A[mid+1]…A[r2]) > min(Al…AMid) • max(A[mid+1]…A[r1−1]) < max(Al…AMid) • [r1, r2]包含了所有可能合法的右端点
• 类似桶排的记录即可 • O(n log n)
这道题真的TM是写吐了 画了三个小时才写完 希望能增强我的分治能力
/*
if you can't see the repay
Why not just work step by step
rubbish is relaxed
to ljq
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
typedef pair<int,int> pll;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int _inf = 0xc0c0c0c0;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll _INF = 0xc0c0c0c0c0c0c0c0;
const ll mod = (int)1e9+7;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll ksm(ll a,ll b,ll mod){int ans=1;while(b){if(b&1) ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;}return ans;}
ll inv2(ll a,ll mod){return ksm(a,mod-2,mod);}
void exgcd(ll a,ll b,ll &x,ll &y,ll &d){if(!b) {d = a;x = 1;y=0;}else{exgcd(b,a%b,y,x,d);y-=x*(a/b);}}//printf("%lld*a + %lld*b = %lld\n", x, y, d);
/*namespace sgt
{
#define mid ((l+r)>>1)
#undef mid
}*/
const int MAX_N = 300025;
ll cnt[MAX_N<<1+5],lmax[MAX_N],lmin[MAX_N],rmax[MAX_N],rmin[MAX_N],n,arr[MAX_N];
ll divide(int l,int r)
{
if(l==r) return 1;
ll ret = 0;
int mid =(l+r)>>1;
lmax[mid] = lmin[mid] = arr[mid],rmax[mid+1] = rmin[mid+1] = arr[mid+1];
for(int i = mid-1;i>=l;--i)
lmax[i] = max(lmax[i+1],arr[i]),lmin[i] = min(lmin[i+1],arr[i]);
for(int i = mid+2;i<=r;++i)
rmax[i] = max(rmax[i-1],arr[i]),rmin[i] = min(rmin[i-1],arr[i]);
for(int i = l;i<=mid;++i)//同侧都在左边
{
int j = lmax[i] - lmin[i] + i;
if(j>mid&&j<=r&&rmin[j]>lmin[i]&&rmax[j]<lmax[i]) ret++;
}
for(int j = mid+1;j<=r;++j)//同侧都在右边
{
int i = j + rmin[j] - rmax[j];
if(i>=l&&i<=mid&&rmin[j]<lmin[i]&&rmax[j]>lmax[i]) ret++;
}
for(int i = mid,r1 = mid+1,r2 = mid + 1;i>=l&&r1<=r;--i)//左小右大
{
while(r2<=r&&rmin[r2]>lmin[i]) cnt[rmax[r2]-r2+MAX_N]++,r2++;
while(r1<=r&&rmax[r1]<lmax[i]) cnt[rmax[r1]-r1+MAX_N]--,r1++;
if(r1<r2) ret+=cnt[lmin[i] -i +MAX_N];
}
for(int i = mid+1;i<=r;++i)
cnt[rmax[i]-i+MAX_N] = 0;
for(int j = mid + 1,l1 = mid ,l2 = mid;j<=r&&l2>=l;++j)//左大右小
{
while(l1>=l&&lmin[l1]>rmin[j]) cnt[lmax[l1]+l1]++,l1--;
while(l2>=l&&lmax[l2]<rmax[j]) cnt[lmax[l2]+l2]--,l2--;
if(l1<l2) ret += cnt[rmin[j]+j];
}
for(int i = l;i<=mid;++i)
cnt[lmax[i]+i] = 0;
return ret+divide(l,mid)+divide(mid+1,r);
}
int main()
{
//ios::sync_with_stdio(false);
//freopen("a.txt","r",stdin);
//freopen("b.txt","w",stdout);
scanf("%lld",&n);
for(int i = 1;i<=n;++i)
{
int x;
ll y;
scanf("%d%lld",&x,&y);
arr[x] = y;
}
ll ans = divide(1,n);
printf("%lld\n",ans);
//fclose(stdin);
//fclose(stdout);
//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
return 0;
}