P5102 [JOI 2016 Final] 领地
P5102 [JOI 2016 Final] 领地 模拟赛题,但是赛时挂在了取模上,就差一点啊啊啊啊啊啊。
记 \((x_i,y_i)\) 是移动了 \(i\) 次后的坐标。
肯定要从周期的方面考虑,每一组操作产生的点是上一组操作产生的点在 \(x\) 轴方向平移了 \(x_n\),\(y\) 轴方向平移了 \(y_n\) 得到的,即 \(\forall i\geq n,(x_i,y_i)=(x_{i\bmod n}+k x_n,y_{i\bmod n}+k y_n)\),其中 \(k=\lfloor \dfrac{i}{n} \rfloor\)。
那我们考虑将所有可以表示为 \((x'+kx_n,y'+ky_n)\) 的点分为一组,每个点也只会分到一组中。在一组中钦定唯一一个位置使其编号即 \(k\) 值为 \(0\),这样每个点都获得了一个编号。
那么我们现在有一个点编号为 \(p\),进行所有 \(K\) 组操作后,这个点覆盖的编号区间即 \([p,p+K-1]\)。我们可以直接在每组中做这个,然后取区间并,就得到了这组点中所有可以覆盖的地方。
现在我们对每组点同时做原问题,其实就相当于找到另外三组点,然后求它们覆盖的区间交。
初始点数是 \(O(n)\) 的,所以组数也是 \(O(n)\) 的,所以区间数也是 \(O(n)\) 的,中间需要一些排序,所以时间复杂度是 \(O(n\log n)\)。
再说一些细节。
\(x_n=y_n=0\) 要特别处理一下,也就是每个点单独是一组,一些写法可能要把 \(K\) 手动改成 \(1\)。
分组相当于每组点在一条直线上,每条直线的斜率也是一样的,所以可以通过截距找出每条直线。截距是 \(y-\dfrac{y_n}{x_n}x\),可以乘一个分母变成整数,同时防止除以 \(0\)。
但是每条直线不一定是一个组,同一截距每个 \(x\equiv x'\pmod {x_n}\) 是一组,\(x_n\) 为 \(0\) 时可以改为用 \(y\) 和 \(y_n\) 做,后面一些东西也是。注意取模的时候负数和正数的取模值是不一样的,要转一下。
这样每组对应一个截距和 \(x\bmod x_n\) 组成的二元组,可以用哈希表存一下。每组可以把第一个加进去的点的 \(k\) 值设为 \(0\)。求每个点 \((x,y)\) 对应的 \(k\) 值直接用 \(x\) 减去第一个点的横坐标再除一下 \(x_n\) 即可。
求区间交的时候,每组点编号并不统一,手动统一一下就行。比方说当前组 \(k=0\) 的点是 \((x,y)\),那么找到 \((x,y+1)\) 在他们组当中的 \(k\) 值 \(k'\),然后每个端点减去 \(k'\),就可以统一编号了。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<unordered_map>
#define ll long long
using namespace std;
const int MAXN=1e5+10;
string s;int T,n,kx,ky,tot;ll ans;
unordered_map <ll,int> H;
struct ple{ll x,y,z;};vector <ple> v;
inline bool cmp(ple x,ple y)
{return x.x!=y.x?x.x<y.x:x.z>y.z;}
inline bool ccmp(ple x,ple y)
{return x.x!=y.x?x.x<y.x:x.y<y.y;}
struct node
{
vector <ple> v,t,f;
inline void work()
{
for(ple now:v) t.push_back({now.z,now.z+(T-1)});
sort(t.begin(),t.end(),ccmp);ll l=t[0].x,r=t[0].y;
for(int i=1;i<t.size();++i)
{
if(r>=t[i].x-1){r=max(r,t[i].y);continue;}
f.push_back({l,r}),l=t[i].x,r=t[i].y;
}
f.push_back({l,r});return ;
}
}t[MAXN];
inline ll M(ll x,ll y){y=abs(y);return (x%y+y)%y;}
inline ll pos(ll x,ll y)
{
if(!kx&&!ky) return x*1000000+y;
return (y*kx-x*ky)*100000+(kx?M(x,kx):M(y,ky));
}
inline ll F(ll x,ll y)
{
if(!kx&&!ky) return 0;
ll num=pos(x,y);int pos=H[num];
ll prex=t[pos].v[0].x,prey=t[pos].v[0].y;
return (kx?(x-prex)/kx:(y-prey)/ky);
}
inline void upd(ll x,ll y)
{
ll num=pos(x,y);if(!H[num])
H[num]=++tot,t[tot].v.push_back({x,y,0});
else
{
if(!kx&&!ky) return ;
int pos=H[num],prex=t[pos].v[0].x,prey=t[pos].v[0].y;
if(kx) t[pos].v.push_back({x,y,(x-prex)/kx});
else t[pos].v.push_back({x,y,(y-prey)/ky});
}
}
signed main()
{
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
cin>>n>>T>>s;
for(char c:s)
{
if(c=='N') ++ky;if(c=='S') --ky;
if(c=='E') ++kx;if(c=='W') --kx;
}
if(!kx&&!ky) T=1;upd(0,0);int x=0,y=0;
for(int i=1;i<=n;++i)
{
char c=s[i-1];
if(c=='N') ++y;if(c=='S') --y;
if(c=='E') ++x;if(c=='W') --x;
upd(x,y);
}
for(int i=1;i<=tot;++i) t[i].work();
for(int i=1;i<=tot;++i)
{
ll x=t[i].v[0].x,y=t[i].v[0].y,s=0,pre;
ll num1=pos(x,y+1);if(!H[num1]) continue;
ll num2=pos(x+1,y);if(!H[num2]) continue;
ll num3=pos(x+1,y+1);if(!H[num3]) continue;
ll pos[4]={i,H[num1],H[num2],H[num3]};
ll del[4]={0,F(x,y+1),F(x+1,y),F(x+1,y+1)};
for(int o=0;o<4;++o) for(ple now:t[pos[o]].f)
v.push_back((ple){now.x-del[o],o,1}),
v.push_back((ple){now.y-del[o],o,0});
sort(v.begin(),v.end(),cmp);
for(ple now:v)
{
if(s==15) ans+=now.x-pre+1;
s^=(1<<now.y),pre=now.x;
}v.clear();
}
cout<<ans<<'\n';return 0;
}