zoukankan      html  css  js  c++  java
  • [GXOI/GZOI2019]特技飞行

    题目链接 ![https://www.luogu.org/problem/P5302]

    思路:这道题可以说是两道题的合并。注意到\(c\)的分数与 \(a\)\(b\)的分数 无关,也就是说可以分成两部分来计算。首先,对于\(c\)的分数,发现就是判断点是否在正方形内。于是可以将坐标轴旋转45°(坐标\((x,y)\)映射为坐标\((x+y,x-y)\)不理解的可以手玩一下--),然后就可以转化为经典的扫描线问题了。而后,对于a和b的分数对极值的贡献,可以发现这取决于「对向交换」的次数。「对向交换」显然可以每次都进行,因为题目中说:“容易发现,「对向交换」会使它们的航线变为折线,并保持它们在纵坐标上的相对顺序;而「擦身而过」会改变它们在纵坐标上的相对顺序。”满足题意。而什么时候「对向交换」次数最少呢?我们可以将每个起点向其目标终点的原对应起点连一条边(在样例1中,\(1--2,2--4,3--3,4--1\)),在这其中会形成环(样例1中\(1--2--4\)以及\(3\)一个自环),每个环都相对独立。对于每个环,其实相当于一个置换,环内需交换次数为:\(环长-1\),且其为最少。因此最少次数为它们的和:\(n-环个数\)。至此,分析就结束了。

    具体实现:对于扫描线的问题,离散化后上树状数组就可以了。对于求环的个数,不用连边,而直接用并查集判断有多少集合即可。至于求交点个数及坐标,我个人是把起点终点坐标撞到\(set\)里面,然后用类似于求逆序对的方法实现的。

    注意事项:数组大小要注意。\(n\)\(k\)不同阶。

    代码:

    #include<bits/stdc++.h>
    #define in read()
    using namespace std;
    const int N=1e5+5;
    const double eps=1e-7;
    int n,a,b,c,k,xst,xed,ans1,ans2,cnt,num,cp,tt,anss;
    int st[N],ed[N],fa[N];
    struct nd{int id,h;}tp[N];
    struct pot{double x,y;int x1;}tmp[N*5];
    struct seg{double he;int v,l,r;}ct[N<<1];
    struct cop{double xx;int e,re;}hh[N*10];
    bool operator<(nd x,nd y){return x.h<y.h;}
    bool operator<(pot x,pot y){return x.y>y.y;}
    bool operator<(seg x,seg y){return x.he>y.he;}
    inline bool cmp(cop x,cop y){return x.xx<y.xx;}
    inline bool cmp1(cop x,cop y){return x.e<y.e;}
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    	return s*w;
    }
    int fd(int x){return fa[x]==x?x:fa[x]=fd(fa[x]);}
    inline pot zg(pot w){return pot{w.x+w.y,w.x-w.y,0};}
    set<nd>tqx;
    inline pot pt(pot a1,pot b1,pot a2,pot b2)
    {
    	double k1=(1.0*(a1.y-b1.y))/(1.0*(a1.x-b1.x)),b11=a1.y-k1*a1.x;
    	double k2=(1.0*(a2.y-b2.y))/(1.0*(a2.x-b2.x)),b22=a2.y-k2*a2.x;
    	if(k1-k2==0) return pot{0,0,0};
    	double x=(b22-b11)/(k1-k2),y=x*k1+b11;
    	return pot{x,y,0};
    }
    inline void pre1()
    {
    	for(int i=1;i<=n;++i)
    	{
    		int fx=fd(i),fy=fd(tp[i].id);
    		if(fx!=fy) fa[fx]=fy;
    	}
    	ans1=n;
    	for(int i=1;i<=n;++i)
    		if(fd(i)==i) --ans1;
    }
    inline void pre2()
    {
    	for(int i=1;i<=n;++i)
    	{
    		set<nd>::iterator it=tqx.upper_bound((nd){i,ed[i]});
    		for(;it!=tqx.end();it++)
    			tmp[++ans2]=pt(pot{1.0*xst,st[i],0},pot{1.0*xed,ed[i],0},pot{1.0*xst,st[it->id],0},pot{1.0*xed,ed[it->id],0});
    		tqx.insert((nd){i,ed[i]});
    	}
    	cnt=ans2;
    }
    struct Tree{
    	int c[N*10];
    	inline int lowbit(int x){return x&(-x);}
    	inline void upd(int x,int v)
    	{
    		while(x<=tt)c[x]+=v,x+=lowbit(x);
    	}
    	inline int query(int x)
    	{
    		int ans=0;
    		while(x>0)ans+=c[x],x-=lowbit(x);
    		return ans;
    	}
    }T;
    inline void work()
    {
    	sort(tmp+1,tmp+cnt+1);
    	sort(ct+1,ct+num+1);
    	int ret=1;
    	while(tmp[ret].y>ct[1].he&&ret<=cnt) ++ret;
    	for(int i=1;i<=num;++i)
    	{
    		while(tmp[ret].y>ct[i].he&&ret<=cnt)
    			anss+=(T.query(tmp[ret++].x1)>0);
    		T.upd(ct[i].l,ct[i].v);
    		T.upd(ct[i].r+1,-ct[i].v);
    	}
    	ans1+=anss*c,ans2+=anss*c;
    	if(ans1>ans2) swap(ans1,ans2);
    	printf("%d %d\n",ans1,ans2);
    }
    int main()
    {
    	n=in,a=in,b=in,c=in,xst=in,xed=in;
    	for(int i=1;i<=n;++i) fa[i]=i;
    	for(int i=1;i<=n;++i) st[i]=in;
    	for(int i=1;i<=n;++i) ed[i]=in;
    	for(int i=1;i<=n;++i) tp[i].h=ed[i],tp[i].id=i;
    	sort(tp+1,tp+n+1);
    	pre1();pre2();
    	ans1=ans1*a+(ans2-ans1)*b;ans2*=a;
    	k=in;
    	for(int i=1;i<=k;++i)
    	{
    		double _x=1.0*read(),_y=1.0*read(),_r=1.0*read();
    		pot pt11=zg(pot{_x,_y-_r,0}),pt12=zg(pot{_x+_r,_y,0});
    		pot pt21=zg(pot{_x-_r,_y,0}),pt22=zg(pot{_x,_y+_r,0});
    		ct[++num].he=pt11.y,ct[num].v=1;
    		ct[++num].he=pt21.y,ct[num].v=-1;
    		hh[++cp].xx=pt11.x,hh[cp].e=cp;
    		hh[++cp].xx=pt12.x,hh[cp].e=cp;
    		hh[++cp].xx=pt21.x,hh[cp].e=cp;
    		hh[++cp].xx=pt22.x,hh[cp].e=cp;
    	}
    	for(int i=1;i<=cnt;++i) tmp[i]=zg(tmp[i]);
    	for(int i=1;i<=cnt;++i)
    		hh[++cp].xx=tmp[i].x,hh[cp].e=cp;
    	sort(hh+1,hh+cp+1,cmp);
    	for(int i=1;i<=cp;++i)
    		hh[i].re=(i==1||(hh[i].xx-hh[i-1].xx>=eps))?++tt:tt;
    	sort(hh+1,hh+cp+1,cmp1);
    	int num2=num<<1;
    	for(int i=1;i<=num2;++i,++i)
    	{
    		int pos=i+1>>1;
    		ct[pos].l=hh[i].re;
    		ct[pos].r=hh[i+1].re;
    	}
    	for(int i=num2+1;i<=cp;++i)
    		tmp[i-num2].x1=hh[i].re;
    	work();
    	return 0;
    }
    
    NO PAIN NO GAIN
  • 相关阅读:
    preprocess
    数组
    共用体
    动态内存管理函数
    C链表
    文件的定位与出错检查
    字符串读写函数
    C文件操作
    位运算
    爱好-超级IP:超级IP
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/11862909.html
Copyright © 2011-2022 走看看