格路计数的一类(降维?)技巧
模拟赛考到了,记录一下。
题面:
给你二维平面上 \(n\) 个点,定义移动:$(x,y) \to (x+1,y) / (x-1,y) / (x,y+1) / (x,y-1) $(一个点可以移动到上下左右与这个点相邻的点)。移动每个点 \(m\) 次,使得最后这些点的终点是同一个点,求移动的合法方案数。\(\bmod\ {10^9+7}\),限制坐标绝对值 \(\le 10^5\),\(n\le 50,m \le 10^5\)。
Sol:
暴力做法:
枚举二维平面上每个点作为终点,再枚举 \(\Delta x\),然后 \(O(m)\) 枚举贡献。时间复杂度 \(O(m^3)\),空间复杂度 \(O(m^2)\)。祭天了。。。。但是你拼上 \(ans=0\) 的答案你会得到整整 \(80\) 分的高分???甚至 \(ans=0\) 一共是 \(50\) 分????????????
std:
考虑将点旋转 \(45\) 度。设新点坐标 \((x',y')\)。
然后发现我们的移动变成了:
\((x',y') \to (x'+1,y'+1) / (x'+1,y'-1)/(x'-1,y'+1) /(x'-1,y'-1)\)
然后发现 \(x,y\) 独立了,可以分开两维,对每一维分别暴力枚举值域计算贡献,最后将两部分贡献乘起来即可。
#include<bits/stdc++.h>
#include<bits/extc++.h>
using __gnu_pbds::cc_hash_table;
#define int long long
using namespace std;
const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar() \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt) \
? EOF \
: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
#ifndef ONLINE_JUDGE
#define ONLINE_JUDGE
#endif
#define ull unsigned long long
const ull mod=1e9+7;
const int N=4e5+5;
ull frac[N],g[N];
ull ksm(ull x,int p)
{
ull ans=1;
while(p)
{
if(p&1) (ans*=x)%=mod;
(x*=x)%=mod;
p>>=1;
}
return ans;
}
int n,m;
int x[N],y[N];
ull ks[N];
ull C(int n,int m)
{
if(m<0||n<0||n-m<0) return 0;
return frac[n]*g[m]%mod*g[n-m]%mod;
}
int calc(int dx)
{
if(dx>m) return 0;
if((m-dx)&1) return 0;
int last=(m-dx)/2;
return C(m,last);
}
int dodp(int *a)
{
int minn=0x3f3f3f3f,maxn=-0x3f3f3f3f;
int ans=0;
for(int i=1;i<=n;i++) minn=min(minn,a[i]),maxn=max(maxn,a[i]);
for(int i=minn-1e5;i<=maxn+1e5;i++)
{
int nw=1;
for(int j=1;j<=n;j++) (nw*=calc(abs(i-a[j])))%=mod;
(ans+=nw)%=mod;
}
return ans;
}
signed main()
{
freopen("wolf.in","r",stdin);
freopen("wolf.out","w",stdout);
ks[0]=1;
frac[0]=g[0]=1;
for(int i=1;i<N;i++) ks[i]=ks[i-1]*2%mod,frac[i]=frac[i-1]*i%mod,g[i]=ksm(frac[i],mod-2);
n=read();
m=read();
for(int i=1;i<=n;i++) x[i]=read(),y[i]=read();
if(n==1) { cout<<ksm(4,m); return 0; }
for(int i=1;i<=n;i++)
{
int a=x[i],b=y[i];
x[i]=a-b;
y[i]=a+b;
}
cout<<dodp(x)*dodp(y)%mod;
return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/p/19256925
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号