T2
R
,
C
≤
200
R,C\leq 200
R,C≤200时,走一步的代价是
D
D
D,跑一边全图spfa即可。
R
,
C
≤
1
0
9
R,C\leq 10^9
R,C≤109的情况,可以把
n
n
n个关键点(加上
(
1
,
1
)
,
(
R
,
C
)
(1,1),(R,C)
(1,1),(R,C))的横纵坐标离散化,先跑出所有横/纵坐标关键的点的最短路。
单独考虑每相邻四个点间的矩形区域,发现只有四个顶点向内有贡献,且每个点的贡献都是一条竖线+一条斜线+一条横线的形式,大力讨论即可。
注意分界线不用二分找,可以
O
(
1
)
O(1)
O(1)算。
这个大力讨论非常的毒瘤,要大力分析+等差数列求和,具体可以看代码:
//start at 4:20 pm end at 7:20pmQWQ
#include<bits/stdc++.h>
#define pb push_back
#define gc getchar
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define sc second
using namespace std;
const int N=210,mod=1e9+7;
typedef long long ll;
typedef double db;
const ll nv6=(mod+1)/6;
int tk,Rx,Cx,n,D,ans,vx[N],vy[N],r,c;
struct P{int x,y,z,ix,iy;}p[N];
ll dis[N][N];
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
char cp;
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1) re=(ll)re*x%mod;
return re;
}
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline int inc(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}
inline int s1(ll x){return (x*(x+1)>>1)%mod;}
inline int s2(ll x){return x*(x+1)%mod*((x<<1)|1)%mod*nv6%mod;}
inline void init()
{
int i;memset(dis,0x7f,sizeof(dis));
rd(Rx);rd(Cx);rd(n);rd(D);r=c=ans=0;
vx[++r]=1;vx[++r]=Rx;vy[++c]=1;vy[++c]=Cx;
for(i=1;i<=n;++i){
rd(p[i].x);rd(p[i].y);rd(p[i].z);
vx[++r]=p[i].x;vy[++c]=p[i].y;
}
sort(vx+1,vx+r+1);sort(vy+1,vy+c+1);
r=unique(vx+1,vx+r+1)-vx-1;c=unique(vy+1,vy+c+1)-vy-1;
}
struct pp{pii pt;ll d;bool operator<(const pp&ky) const{return d>ky.d;}}tp;
priority_queue<pp>que;
inline void upd(int x,int y,ll z){if(dis[x][y]>z) que.push((pp){mkp(x,y),(dis[x][y]=z)});}
inline bool inG(int x,int y){return (x>0 && x<=r && y>0 && y<=c);}
inline void dj()
{
int i,x,y,ix,iy;
for(;!que.empty();){
tp=que.top();que.pop();x=tp.pt.fi;y=tp.pt.sc;
if(dis[x][y]!=tp.d) continue;
for(i=0;i<4;++i)
if(inG((ix=x+dx[i]),(iy=y+dy[i])))
upd(ix,iy,tp.d+(ll)D*abs(vx[ix]-vx[x]+(vy[iy]-vy[y])));
}
}
/*
only calculate
*****
*...*
*...*
*****
the central part
*/
inline int cal1(ll a,ll b,ll c,ll d,int n,int m)
{
ll u=min((ll)n-1,((d-a+(ll)n*D)/(D<<1))),v=min((ll)m-1,(b-a+(ll)m*D)/(D<<1)),w=(c-a+(ll)(n+m)*D)/(D<<1);
if(u<=0 || v<=0 || w<=1) return 0;
w=min(w,u+v);u=min(u,w-1);v=min(v,w-1);a%=mod;if(u>v) swap(u,v);
int re=inc((a+D)*s1(u)%mod,(ll)D*s2(u)%mod);
ad(re,inc((v-u)*inc(a,(u+1)*D%mod)%mod,(ll)D*s1(v-u)%mod)*u%mod);//w-l-1
if(v+1<w){
ad(re,dc((u+v+1)*(w-v-1)%mod,dc(s1(w),s1(v+1)))*a%mod);
ad(re,dc((u+v+1)*dc(s1(w),s1(v+1))%mod,dc(s2(w),s2(v+1)))*(ll)D%mod);
}
return re;
}
inline int cal3(ll a,ll b,int n)
{
ll u=min((ll)n-1,(b-a+(ll)n*D)/(2*D));
if(u<=0) return 0;
return inc(a%mod*u%mod,(ll)s1(u)*D%mod);
}
inline int cal2(ll a,ll b,int n)
{return inc(cal3(a,b,n),cal3(b,a-1,n));}
inline int cal(ll a,ll b,ll c,ll d,int n,int m)
{
int re=cal1(a,b,c,d,n,m);ad(re,cal1(b,c,d,a-1,m,n));
ad(re,cal1(c,d,a-1,b-1,n,m));ad(re,cal1(d,a-1,b-1,c-1,m,n));
return re;//-1避免交界处算重
}
void sol()
{
int i,j,x,y;init();
for(i=1;i<=n;++i){
p[i].ix=x=lower_bound(vx+1,vx+r+1,p[i].x)-vx;
p[i].iy=y=lower_bound(vy+1,vy+c+1,p[i].y)-vy;
upd(x,y,p[i].z);
}
dj();
for(i=1;i<=n;++i)
if(dis[p[i].ix][p[i].iy]!=p[i].z)
{puts("IMPOSSIBLE");return;}
for(i=1;i<=r;++i)
for(j=1;j<=c;++j)
ans=(dis[i][j]+ans)%mod;
for(i=1;i<r;++i)
for(j=1;j<c;++j)
ad(ans,cal(dis[i][j],dis[i][j+1],dis[i+1][j+1],dis[i+1][j],vx[i+1]-vx[i],vy[j+1]-vy[j]));
for(i=1;i<r;++i)
for(j=1;j<=c;++j)
ad(ans,cal2(dis[i][j],dis[i+1][j],vx[i+1]-vx[i]));
for(i=1;i<=r;++i)
for(j=1;j<c;++j)
ad(ans,cal2(dis[i][j],dis[i][j+1],vy[j+1]-vy[j]));
printf("%d\n",ans);
}
int main(){
// freopen("geographer.in","r",stdin);
// freopen("geographer.out","w",stdout);
for(rd(tk);tk;--tk) sol();
fclose(stdin);fclose(stdout);
return 0;
}
T3
做法没啥说的,就是容斥找所有不合法的三角形:
设点
i
i
i指向原点的有向直线为
l
i
l_i
li,设所有在
l
i
l_i
li右侧或
l
i
l_i
li上的点(除
i
i
i)个数为
c
n
t
i
cnt_i
cnti。
a
n
s
=
(
n
3
)
−
∑
(
c
n
t
i
2
)
ans={n\choose 3}-\sum {cnt_i\choose 2}
ans=(3n)−∑(2cnti)
注意这里是没有三点共线的情况,三点贡献还要map另存斜率容斥一下。
代码有很多 t r i c k trick trick:
- double又慢又有精度误差,可以直接记斜率的最简分数表示 y x \dfrac{y}{x} xy的数对 ( x , y ) (x,y) (x,y)
- splay插入时可以叉积判断左右,但这样只能判断同 y > 0 y>0 y>0或同 y < 0 y<0 y<0的两条线的相对关系
- 但每次查的都是整个一半的平面,如下图:
可以把 y < = 0 y<=0 y<=0的部分 x ∗ = − 1 , y ∗ = − 1 x*=-1,y*=-1 x∗=−1,y∗=−1翻上来,刚好拼成整个第一二象限,可以splay往下找的时候左右查找处理操作。 - 于是只维护第一二象限,splay每个结点上分别记录 y > 0 y>0 y>0的 ( x , y ) (x,y) (x,y)相同的点的信息,和翻上来 ( − x , − y ) (-x,-y) (−x,−y)相同的点的信息。
复杂度 O ( n log n ) O(n\log n) O(nlogn)
最粗暴的double+splay提取区间赋值/求和的速度似乎比暴力还慢。。。
#include<bits/stdc++.h>
#define gc getchar
#define F(x) t[x].fa
#define lc(x) t[x].ch[0]
#define rc(x) t[x].ch[1]
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define sc second
using namespace std;
const int N=4e5+12;
typedef long long ll;
int n,rt,cnt,orz,cot,typ;
ll ans;pii nw;
map<pii,int>exi;
char cp;
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
inline void prit(ll x){if(x>9) prit(x/10);putchar('0'+(x%10));}
inline int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
inline ll cal(int n,int op)
{
if(op==2) return (ll)n*(n-1)>>1;
return (ll)n*(n-1)*(n-2)/6;
}
inline bool cmp(pii a,pii b){return (ll)a.fi*b.sc>(ll)a.sc*b.fi;}
struct node{
int v[2],lzy[2],fa,ch[2];
int slf[2],sz[2];
ll sum[2];pii p;
}t[N];
inline void pu(int x)
{
if(!x) return;int i,l=lc(x),r=rc(x);
for(i=0;i<2;++i)
t[x].sz[i]=t[l].sz[i]+t[x].slf[i]+t[r].sz[i],
t[x].sum[i]=t[l].sum[i]+(ll)t[x].v[i]*t[x].slf[i]+t[r].sum[i];
}
inline void col(int x,int tp,int vv)
{
if(!x) return;
t[x].v[tp]+=vv;t[x].lzy[tp]+=vv;
t[x].sum[tp]+=(ll)vv*t[x].sz[tp];
}
inline void pd(int x)
{
if(!x) return;
for(int i=0;i<2;++i) if(t[x].lzy[i]){
col(lc(x),i,t[x].lzy[i]);col(rc(x),i,t[x].lzy[i]);
t[x].lzy[i]=0;
}
}
inline void rot(int x)
{
int y=F(x),z=F(y),dr=(rc(y)==x);
t[y].ch[dr]=t[x].ch[dr^1];
if(t[y].ch[dr]) F(t[y].ch[dr])=y;
F(x)=z;if(z) t[z].ch[rc(z)==y]=x;
F(y)=x;t[x].ch[dr^1]=y;pu(y);
}
int stk[N],top;
inline void splay(int x,int gl)
{
if(!gl) rt=x;int y,z;
for(y=x;y;y=F(y)) stk[++top]=y;
for(;top;--top) pd(stk[top]);
for(;F(x)!=gl;rot(x)){
y=F(x);z=F(y);
if(z!=gl)
((rc(z)==y)^(rc(y)==x))?rot(x):rot(y);
}
pu(x);
}
inline void ins(int &x,int fr)
{
if(!x){
x=++cnt;F(x)=fr;
t[x].slf[typ]=1;t[x].p=nw;t[x].v[typ]=cot;orz=x;
}else if(nw==t[x].p){
pd(x);
ans+=(t[x].sum[0^typ]-t[lc(x)].sum[0^typ])+(t[x].sum[1^typ]-t[rc(x)].sum[1^typ]);
cot+=(t[x].sz[1^typ]-t[lc(x)].sz[1^typ])+(t[x].sz[0^typ]-t[rc(x)].sz[0^typ]);
col(lc(x),1^typ,1);col(rc(x),0^typ,1);orz=x;t[x].v[0]++;t[x].v[1]++;
t[x].slf[typ]++;if(t[x].slf[typ]==1) t[x].v[typ]=cot;
}else{
pd(x);
if(cmp(nw,t[x].p)){
ans+=(t[x].sum[0^typ]-t[lc(x)].sum[0^typ]);
cot+=(t[x].sz[1^typ]-t[lc(x)].sz[1^typ]);
col(rc(x),0^typ,1);t[x].v[0^typ]++;ins(lc(x),x);
}else{
ans+=(t[x].sum[1^typ]-t[rc(x)].sum[1^typ]);
cot+=(t[x].sz[0^typ]-t[rc(x)].sz[0^typ]);
col(lc(x),1^typ,1);t[x].v[1^typ]++;ins(rc(x),x);
}
}
pu(x);
}
void upd(int x,int y)
{
int g=abs(gcd(x,y));x/=g,y/=g;typ=0;
if(y<0 || (y==0 && x<0)) typ=1,x*=-1,y*=-1;
if(!y) x=1;if(!x) y=1;nw=mkp(x,y);
if((cot=++exi[nw])>2) ans+=cal(cot-1,2); //特判共线的情况
cot=0;ins(rt,0);
ans+=cal(cot,2);splay(orz,0);
}
int main(){
// freopen("philosopher.in","r",stdin);
// freopen("philosopher.out","w",stdout);
int i,j,x,y;rd(n);
for(i=1;i<=n;++i){
rd(x);rd(y);upd(x,y);
prit(cal(i,3)-ans);putchar('\n');
}
// fclose(stdin);fclose(stdout);
return 0;
}
总结
T2
看到限制转欧几里得距离再转最短路是必须的,后面就是大力讨论了。
(然而最短路都没想到,真是zz)
T3
代码还是得写得简洁优秀一点,不然连暴力都不如(又难写又慢)