【TEST190323】思维题&数数判断 + 套路算几 splay技巧

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ https://blog.csdn.net/corsica6/article/details/88827981

T2

R , C 200 R,C\leq 200 时,走一步的代价是 D D ,跑一边全图spfa即可。
R , C 1 0 9 R,C\leq 10^9 的情况,可以把 n n 个关键点(加上 ( 1 , 1 ) , ( R , C ) (1,1),(R,C) )的横纵坐标离散化,先跑出所有横/纵坐标关键的点的最短路。

单独考虑每相邻四个点间的矩形区域,发现只有四个顶点向内有贡献,且每个点的贡献都是一条竖线+一条斜线+一条横线的形式,大力讨论即可。
在这里插入图片描述
注意分界线不用二分找,可以 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 指向原点的有向直线为 l i l_i ,设所有在 l i l_i 右侧或 l i l_i 上的点(除 i i )个数为 c n t i cnt_i
a n s = ( n 3 ) ( c n t i 2 ) ans={n\choose 3}-\sum {cnt_i\choose 2}

注意这里是没有三点共线的情况,三点贡献还要map另存斜率容斥一下。

代码有很多 t r i c k trick

  • double又慢又有精度误差,可以直接记斜率的最简分数表示 y x \dfrac{y}{x} 的数对 ( x , y ) (x,y)
  • splay插入时可以叉积判断左右,但这样只能判断同 y &gt; 0 y&gt;0 或同 y &lt; 0 y&lt;0 的两条线的相对关系
  • 但每次查的都是整个一半的平面,如下图:
    在这里插入图片描述
    可以把 y &lt; = 0 y&lt;=0 的部分 x = 1 , y = 1 x*=-1,y*=-1 翻上来,刚好拼成整个第一二象限,可以splay往下找的时候左右查找处理操作。
  • 于是只维护第一二象限,splay每个结点上分别记录 y &gt; 0 y&gt;0 ( x , y ) (x,y) 相同的点的信息,和翻上来 ( x , y ) (-x,-y) 相同的点的信息。

复杂度 O ( n log n ) O(n\log n)

最粗暴的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
代码还是得写得简洁优秀一点,不然连暴力都不如(又难写又慢)

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/88827981