我颓代码了,我不是人
要是不颓代码我绝对不会想到树状数组可以这么搞
$n<=10000,m<=2500$
题解
$2^n$算法,枚举子集
$n^4$算法,枚举四个点,这样绝对不重不漏
$n^2*log$算法,一层循环枚举$l$这一列,一层循环枚举$r$,这一列
考虑如何计算卡在$l,r$之间的值,考虑枚举上边界(这里所说的上边界就是$l$对应列,$r$对应列上的所有点,按照$y$排序后从小到大枚举点)
数字是枚举顺序
考虑上边界和下边界之间贡献
设上边界$y1$,下边界$y2$,之间点个数为$cnt$,下面有$w$个点
之间每个点都会与下面点形成新的矩形,那么这样贡献就是$\sum\limits_{ynow}^{y1<=ynow<=y2}$ $\sum\limits_{ypre}^{ypre
那么现在我们要快速查$ynow$-$ypre$不同值的和
树状数组即可
这里,树状数组实现很$sb$,然后求出来这之间$y$之和,再$-$下面所有$y$下标,这样得到了真实长度,
给一下实现,$ask2$是求和,$ask1$是求个数
1 (ans+=((ask2(nxt-1)-ask2(cur-1))*ask1(down)%mod-((ask1(nxt-1)-ask1(cur-1)))*ask2(down)%mod)*(i-j))%mod;
$(ask2(nxt-1)-ask2(cur-1))*ask1(down)$是求出来这一段下标之和,每一个下面的点都会有$ask2(nxt-1)-ask2(cur-1)$贡献,$(ask1(nxt-1)-ask1(cur-1)))*ask2(down)$是求出来下面下标和,每一个点下面下标和即为$ask2$
大致长这样
这里实现还有一些小注意点,
1.枚举$r$然后你从大到小枚举$l$这样你每次树状数组不用再清空可以重复利用上次值
2.枚举上边界可以单调指针扫
代码
#include
using namespace std;
#define ll long long
#define A 2510
#define fi first
#define se second
#define m 2500
const ll mod=1e9+7;
ll f[A][A],cnt[A],sum[A],vec[A][A],c[A],vis[A];
ll ans,n;
ll ask1(ll x){
ll ans=0;
for(ll i=x;i>=1;i-=i&-i)
ans+=cnt[i];
return ans;
}
ll ask2(ll x){
ll ans=0;
for(ll i=x;i>=1;i-=i&-i)
ans+=sum[i];
return ans;
}
//1个数,2总和
void add1(ll x,ll d){
for(ll i=x;i<=m;i+=i&-i)
cnt[i]+=d;
}
void add2(ll x,ll d){
for(ll i=x;i<=m;i+=i&-i)
sum[i]+=d;
}
void update(ll x){
if(vis[x]) return ;
//printf("i=%lld\n",x),
vis[x]=1;
add1(x,1);add2(x,x);
}
void init(){
memset(sum,0,sizeof(sum));
memset(cnt,0,sizeof(cnt));
memset(vis,0,sizeof(vis));
}
int main(){
// freopen("da.in","r",stdin);
// freopen("ans.bf","w",stdout);
scanf("%lld",&n);
for(ll i=1,x,y;i<=n;i++){
scanf("%lld%lld",&x,&y);
vec[x][++c[x]]=y;
}
for(ll i=1;i<=m;i++){
sort(vec[i]+1,vec[i]+c[i]+1);
vec[i][c[i]+1]=m+1;
}
for(ll i=1;i<=m;i++){//枚举上边界,然后更新数组
printf("%lld\n",i);
if(c[i]){
init();
for(ll j=1;j<=c[i];j++) update(vec[i][j]);
for(ll j=i-1;j>=1;j--){
// printf("j=%lld\n",j);
if(c[j]){
ll ita=1,itb=1,cur=max(vec[i][ita],vec[j][itb]);
for(ll k=1;k<=c[j];k++) update(vec[j][k]);
// printf("ita=%lld itb=%lld cur=%lld\n",ita,itb,cur);
while(vec[i][ita+1]<=cur) ita++;
while(vec[j][itb+1]<=cur) itb++;
while(ita<=c[i]&&itb<=c[j]){
ll nxt=min(vec[i][ita+1],vec[j][itb+1]),down=min(vec[i][ita],vec[j][itb]);//上边界
(ans+=((ask2(nxt-1)-ask2(cur-1))*ask1(down)%mod-((ask1(nxt-1)-ask1(cur-1)))*ask2(down)%mod)*(i-j))%mod;
//sb容斥
cur=nxt;
if(vec[i][ita+1]<=cur) ita++;
if(vec[j][itb+1]<=cur) itb++;
}
}
}
}
}
printf("%lld\n",ans);
}
View Code