1 条题解

  • 0
    @ 2025-8-24 21:34:34

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar K8He
    あぁ! 夏を今もう一回

    搬运于2025-08-24 21:34:34,当前版本为作者最后更新于2022-04-09 20:35:19,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    P2154 虔诚的墓主人 题解

    原题传送门

    题意

    n×mn\times m 一个方格上给你 ww 个点,求方格里每个点正上下左右各选 kk 个点的方案数。

    $1\le N,M\le 1,000,000,000,0 \le x_i \le N,0 \le y_i \le M,1 \le W \le 100,000,1 \le k \le 10$。

    思路

    首先看到 N,MN,M 这么大,肯定要先离散化一下。

    然后考虑怎么求方案数。

    我们先对离散化后的点排个序,然后考虑两个 xx 相同的点 x,y1x,y1x,y2x,y2 之间的所有点的方案数。

    显然是:

    $$C_{y1\_UP}^{k}\times C_{y2\_DOWN}^{k}\times \sum_{y1<l<y2}C_{l\_LEFT}^{k}\times C_{l\_RIGHT}^{k} $$

    你们意会一下。

    观察这个式子,Cy1_UPk×Cy2_DOWNkC_{y1\_UP}^{k}\times C_{y2\_DOWN}^{k} 当前已知,可以用前缀和维护 Cl_LEFTk×Cl_RIGHTk\sum C_{l\_LEFT}^{k}\times C_{l\_RIGHT}^{k}

    那么我们就开一个树状数组,维护前 ii 行的 Cl_LEFTk×Cl_RIGHTkC_{l\_LEFT}^{k}\times C_{l\_RIGHT}^{k} 之和,每次碰到一个点 x,yyx,yy 时把当前行的影响清除,再令 yy_LEFT+1,yy_RIGHT1yy\_LEFT+1,yy\_RIGHT-1,再重新计入前缀和。

    可以参考代码中 Solve 函数中变量 uu 的求法。

    时间复杂度 O(nlogn)O(nlogn)

    我这个菜鸡居然因为取模取错了调了两节课。

    Code

    #include <bits/stdc++.h>
    #define _for(i,a,b) for(ll i=a;i<=b;++i)
    #define for_(i,a,b) for(ll i=a;i>=b;--i)
    #define ll long long
    using namespace std;
    const ll N=1e5+10,P=2147483648;
    ll n,m,w,k,q[N],h[N],z[N],y[N],ans;
    struct tree{ll x,y;}t[N];
    inline ll rnt(){
    	ll x=0,w=1;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*w;
    }
    namespace SZSZ{
    	/*树状数组*/
    	ll b[N];
    	inline ll lowbit(ll x){return x&-x;}
    	inline void UpDate(ll x,ll y){
    		while(x<=w){
    			b[x]=(b[x]+y)%P;
    			x+=lowbit(x);
    		}
    		return;
    	}
    	inline ll Query(ll x){
            if(x==0)return 0;
    		ll sum=0;
    		while(x){
    			sum=(sum+b[x])%P;
    			x-=lowbit(x);
    		}
    		return sum;
    	}
    }
    namespace LISAN{
    	/*离散化*/
    	vector<ll>xx,yy;
    	inline bool cmp(tree a,tree b){
    		if(a.x==b.x)return a.y<b.y;
    		return a.x<b.x;
    	}
    	inline void Add(ll x,ll y){
    		xx.push_back(x);
    		yy.push_back(y);
    		return;
    	}
    	inline void LiSan(){
    		sort(xx.begin(),xx.end());
    		sort(yy.begin(),yy.end());
    		xx.erase(unique(xx.begin(),xx.end()),xx.end());
    		yy.erase(unique(yy.begin(),yy.end()),yy.end());
    		_for(i,1,w){
    			t[i].x=lower_bound(xx.begin(),xx.end(),t[i].x)-xx.begin()+1;
    			t[i].y=lower_bound(yy.begin(),yy.end(),t[i].y)-yy.begin()+1;
    			++h[t[i].x],++y[t[i].y];
    		}
    		sort(t+1,t+w+1,cmp);
    		return;
    	}
    }
    namespace SOLVE{
    	ll c[N*20][20]={0};
    	/*预处理组合数*/
    	inline void PreC(){
    		c[0][0]=1;
    		_for(i,1,w){
    			c[i][0]=1;
    			_for(j,1,min(k,i))
    				c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
    		}
    	}
    	/*求解*/
    	inline ll Solve(){
    		PreC();
    		_for(i,1,w-1){
    			++q[t[i].x];
    			++z[t[i].y];
    			if(t[i].x==t[i+1].x&&q[t[i].x]>=k&&h[t[i].x]-q[t[i].x]>=k){
    				ll up=c[q[t[i].x]][k];
    				ll dn=c[h[t[i].x]-q[t[i].x]][k];
    				ll ri=SZSZ::Query(t[i+1].y-1)-SZSZ::Query(t[i].y);
    				ans+=((up*dn+P)%P*ri+P)%P;
                    ans%=P;
    			}
    			ll u=((c[z[t[i].y]][k]*c[y[t[i].y]-z[t[i].y]][k]+P)%P-(SZSZ::Query(t[i].y)-SZSZ::Query(t[i].y-1)+P)%P+P)%P;
    			SZSZ::UpDate(t[i].y,u);
    		}
    		return ans;
    	}
    }
    int main(){
    	n=rnt(),m=rnt(),w=rnt();
    	_for(i,1,w){
    		t[i].x=rnt(),t[i].y=rnt();
    		LISAN::Add(t[i].x,t[i].y);
    	}
    	k=rnt();
    	LISAN::LiSan();
    	printf("%lld\n",SOLVE::Solve());
    	return 0;
    }
    /*
    
    */
    
    • 1

    信息

    ID
    1157
    时间
    800ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者