我颓代码了,我不是人
要是不颓代码我绝对不会想到树状数组可以这么搞
$n<=10000,m<=2500$
题解
$2^n$算法,枚举子集
$n^4$算法,枚举四个点,这样绝对不重不漏
$n^2*log$算法,一层循环枚举$l$这一列,一层循环枚举$r$,这一列
考虑如何计算卡在$l,r$之间的值,考虑枚举上边界(这里所说的上边界就是$l$对应列,$r$对应列上的所有点,按照$y$排序后从小到大枚举点)
数字是枚举顺序
考虑上边界和下边界之间贡献
设上边界$y1$,下边界$y2$,之间点个数为$cnt$,下面有$w$个点
之间每个点都会与下面点形成新的矩形,那么这样贡献就是$sumlimits_{ynow}^{y1<=ynow<=y2}$ $sumlimits_{ypre}^{ypre<y1} (ynow -ypre)*(r-l)$(之前每个矩形都可以扩大这些)
那么现在我们要快速查$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<bits/stdc++.h> 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 ",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 ",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 ",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 ",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 ",ans); }