zoukankan      html  css  js  c++  java
  • 集训队作业选讲

    名字十分高大上,实际上是机房真正的巨佬 ( ext{hehezhou}) 的集训队作业选讲,蒟蒻只能做摘抄。

    GYM100801G Graph

    这是一道思维题,代码难度不高。

    我们先考虑 (k=0) 时怎么做,就是拿一个小根堆去瞎搞一下就可以了。然后我们考虑加边的问题。

    比较显然的,我肯定是要利用字典序大的点去限制字典序小的点,同时要尽量使前面的点大(废话),所以我们可以考虑一边算答案一边贪心,因为他前面的抉择更优就会使得答案更优,不存在前面劣后面优的情况。

    我们考虑能在前面加边就加边,同时使连向这个点的点权尽量大。比较显然的,我们仅通过一条边就完全可以控制这个点再最终 (top) 序中的位置。所以,我们考虑再加入一个大根堆,把能连边的点都放进去,然后在不能继续加边的情况就弹出大根堆里的点(注意是能连边的点,此时还并未确定谁连向他),使得他作为当前 (top) 序的末尾,同时更新小根堆,然后重复上述过程即可。具体的判断条件可见代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5,M=1e5+5;
    int n,m,k;
    struct Edge{int nxt,to;}e[M];int fir[N],d[N];
    void add(int u,int v,int i){e[i]={fir[u],v},fir[u]=i;}
    priority_queue<int,vector<int>,greater<int> > q1;
    priority_queue<int> q2;//less
    vector<int> res;
    vector<pair<int,int> > ans;
    int main()
    {
    	freopen("graph.in","r",stdin);
    	freopen("graph.out","w",stdout);
        //有文操,很恶心,我因为这个交了十几发。
    	cin>>n>>m>>k;
    	for(int i=1,u,v;i<=m;++i) scanf("%d%d",&u,&v),add(u,v,i),d[v]++;
    	for(int i=1;i<=n;++i) if(!d[i]) q1.push(i);
    	while((int)res.size()<n)
    	{
    		int tmp;bool tag=0;
    		if(q1.empty()) tmp=q2.top(),q2.pop(),tag=1;//如果小根堆为空,那么只能取出大根堆中的点连
    		else
    		{
    			tmp=q1.top(),q1.pop();//如果小根堆中有点,就取出连边
    			if(k&&!q1.empty()){q2.push(tmp),k--;continue;}//如果取出后小根堆不空,那么当前点必定可以被小根堆中剩余的点连边
    			if(k&&!q2.empty()&&q2.top()>tmp){q2.push(tmp),k--;continue;}//或者说可以被大根堆里的点连边
    		}
    		// printf("%d %d
    ",tmp,tag);
    		if(tag)	ans.push_back(make_pair(res.back(),tmp));
    		res.push_back(tmp);//否则就只能丢入top序中了
    		for(int i=fir[tmp];i;i=e[i].nxt)
    		{
    			d[e[i].to]--;
    			if(!d[e[i].to]) q1.push(e[i].to);
    		}
    	}
    	for(int i=0;i<(int)res.size();++i) printf("%d ",res[i]);
    	printf("
    %d
    ",(int)ans.size());
    	for(int i=0;i<(int)ans.size();++i) printf("%d %d
    ",ans[i].first,ans[i].second);
    	return 0;
    }
    

    GYM100299E Escape

    据大佬说,这是一道套路题。

    具体的套路就是你可以将每一个点转化为点对 ((x,y)) 表示走到当前点,至多会在一个时刻最小为 (x(x<0)) ,同时最终获得的值为 (x+y) ,我们有了这个东西之后就可以考虑合并了,那么在所有点合并之后的 (x) 就可以判断答案了。

    合并的公式比较显然,大家可以根据定义自己推一推:

    [egin{cases} x'=min(x_1,x_1+y_1+x_2)\ y'=x_1+y_1+x_2+y_2-x' end{cases} ]

    根据定义和公式可以比较显然的发现,这个运算是不满足交换律的,所以计算的先后顺序决定了最后 (x) 的大小,而我们要使得最后的 (x) 越大越好 ((x<0))

    我们可以分类讨论一下:

    1. 对于 (x_1+y_1<0)(x_2+y_2>0) 的情况。

    [x'=maxegin{cases} min(x_1,x_1+y_1+x_2)\ min(x_2,x_2+y_2+x_1) end{cases} ]

    [egin{array}{c} ecause x_2+y_2>0~ ext{且}~x_1+y_1<0\ herefore x_2+y_2+x_1>x_1\ x_2>x_1+y_1+x_2\ herefore x'=min(x_2,x_2+y_2+x_1) end{array} ]

    所以必定先取 (x+y>0) 的。

    1. 对于 (x_1+y_1<0) , (x_2+y_2<0)(y_1<y_2) 的情况。

    [x'=maxegin{cases} min(x_1,x_1+y_1+x_2)\ min(x_2,x_2+y_2+x_1) end{cases} ]

    [egin{array}{c} ecause x_1+y_1<0,x_2+y_2<0~ ext{且}~y_1<y_2\ herefore egin{cases} x_1>x_2+y_2+x_1\ x_2>x_1+y_1+x_2\ x_2+y_2+x_1>x_1+y_1+x_2\ end{cases}\ herefore x'=min(x_2,x_2+y_2+x_1) end{array} ]

    所以两个都为负的,取 (y) 大的。

    1. 对于 (x_1+y_1>0) , (x_2+y_2>0)(x_1<x_2) 的情况。

    [x'=maxegin{cases} min(x_1,x_1+y_1+x_2)\ min(x_2,x_2+y_2+x_1) end{cases} ]

    [egin{array}{c} ecause x_1+y_1>0,x_2+y_2>0~ ext{且}~x_1<x_2\ herefore egin{cases} x_1<x_2+y_2+x_1\ x_2<x_1+y_1+x_2\ x_2>x_1\ end{cases}\ herefore x'=min(x_2,x_2+y_2+x_1) end{array} ]

    所以两个都为正的,取 (x) 大的。

    然后再将每一个点都按照优先度塞入一个堆里,依次取出与父亲合并,最后再看一下根结点的 (x) 值就可以了。

    由于这道题是到达一个点就可以了,所以我们可以将目标点的 (y) 设为 (INF) ,就可以保证答案正确了。

    updata

    刚刚 ( ext{Wallace})的代码出现了一些玄学错误,我们就好好研究了一下 ( ext{STL}) 的神奇之处,需要注意:

    ( ext{STL}) 的比较函数必须严格弱序

    同时,这道题目在处理过程中必须要即时删除父亲,不然就会发生一些蜜汁错误(比如全是 (0) ),导致 (TLE)

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    #define Lint long long
    const Lint INF=1e18+7e9;
    const int N=2e5+5;
    int t,n,ed;
    struct Edge{int nxt,to;}e[N<<1];int fir[N];
    void add(int u,int v,int i){e[i]={fir[u],v},fir[u]=i;}
    struct Uni_Find_Set
    {
    	int fa[N];
    	void init(int n){for(int i=1;i<=n;++i)fa[i]=i;}
    	int find(int x){return (fa[x]!=x)?fa[x]=find(fa[x]):x;}
    	void merge(int u,int v)
    	{
    		int fu=find(u),fv=find(v);
    		if(fu!=fv) fa[fv]=fu;
    	}
    }ufs;
    struct Data{Lint x,y;};
    bool operator < (const Data a,const Data b)
    {
    	int aa=(a.x+a.y>=0);
    	int bb=(b.x+b.y>=0);
    	if(aa!=bb) return aa<bb;
    	if(aa) return (a.x!=b.x)?(a.x<b.x):(a.y<b.y);
    	return (a.y!=b.y)?(a.y<b.y):(a.x<b.x);
    }
    bool operator != (const Data a,const Data b){return (a<b)||(b<a);}
    Data operator + (const Data a,const Data b)
    {
    	Data ans;
    	ans.x=min(a.x,a.x+a.y+b.x);
    	ans.y=a.x+a.y+b.x+b.y-ans.x;
    	return ans;
    }
    struct Node{Data data;int id,fa;}tr[N];
    bool operator < (const Node a,const Node b)
    {
    	if(a.data!=b.data)
    	return a.data<b.data;
    	return a.id<b.id;
    }
    set<Node> q;
    void dfs(int u)
    {
    	for(int i=fir[u];i;i=e[i].nxt)
    	{
    		if(e[i].to==tr[u].fa) continue;
    		tr[e[i].to].fa=u;
    		dfs(e[i].to);
    	}
    }
    void solve()
    {
    	// printf("
    ---------------
    ");
    	cin>>n>>ed;
    	for(int i=1;i<=n;++i)
    	{
    		Lint x;
    		scanf("%lld",&x);
    		tr[i].id=i,tr[i].fa=0;
    		tr[i].data.x=tr[i].data.y=0;
    		if(x>0) tr[i].data.y=x;
    		else tr[i].data.x=x;
    	}
    	tr[ed].data.y+=INF;
    	for(int i=1;i<=n;++i) fir[i]=0;
    	for(int i=1,u,v;i<n;++i)
    	{
    		scanf("%d%d",&u,&v);
    		add(u,v,i<<1),add(v,u,i<<1|1);
    	}
    	dfs(1),ufs.init(n);
    	#define It set<Node>::iterator
    	for(int i=1;i<=n;++i) q.insert(tr[i]);
    	while(!q.empty())
    	{
    		It tmp=q.end();tmp--;
    		// printf("%d %d %lld %lld
    ",tmp->id,tmp->fa,tmp->data.x,tmp->data.y);
    		if(tmp->id!=1)
    		{
    			int ftmp=ufs.find(tmp->fa);
    			if(q.find(tr[ftmp])!=q.end())
    			q.erase(q.find(tr[ftmp]));
    			tr[ftmp].data=tr[ftmp].data+tmp->data;
    			q.insert(tr[ftmp]);
    			ufs.merge(ftmp,tmp->id);
    		}
    		q.erase(tmp);
    	}
    	#undef It
    	if(tr[1].data.x<0) printf("trapped
    ");
    	else printf("escaped
    ");
    	return;
    }
    int main()
    {
    	cin>>t;
    	while(t--) solve();
    	return 0;
    }
    

    GYM102482H Single Cut of Failure

    又是一道思维题,同时也有一种比较常见的处理方式。

    通过观察,我们可以比较显然的发现,答案最多为 (2) ,方法是按两个对角线切割。然后我们只需要判断答案是否为 (1) 即可。

    但是如何判断两个线段是否有交呢?我们可以将所有线段根据整一个框变成区间,然后我们发现,如果两个线段有交,那其中一个线段所变成的区间的端点有且仅有一个位于另一个线段的区间里。

    然后根据这一点,我们就可以双指针去判断是否存在一个区间与所有线段都有交即可。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    int n,w,h;
    struct Line{int l,r;}a[N];
    int uni[N<<1],len=0;
    map<int,int> mp;
    int sum[N<<1],cnt1=0,cnt2=0;
    vector<int> bag[N<<2];
    int check(double x)
    {
    	if(0<=x&&x<=h) return 1;
    	if(h<=x&&x<=h+w) return 2;
    	if(h+w<=x&&x<=h+w+h) return 3;
    	return 4;
    }
    int main()
    {
    	cin>>n>>w>>h;
    	for(int i=1,x,y;i<=n;++i)
    	{
    		scanf("%d%d",&x,&y);
    		if(x==0) a[i].l=y;
    		if(y==h) a[i].l=h+x;
    		if(x==w) a[i].l=h+w+(h-y);
    		if(y==0) a[i].l=h+w+h+(w-x);
    		scanf("%d%d",&x,&y);
    		if(x==0) a[i].r=y;
    		if(y==h) a[i].r=h+x;
    		if(x==w) a[i].r=h+w+(h-y);
    		if(y==0) a[i].r=h+w+h+(w-x);
    	}
    	for(int i=1;i<=n;++i) uni[++len]=a[i].l,uni[++len]=a[i].r;
    	sort(uni+1,uni+1+len),len=unique(uni+1,uni+1+len)-uni-1;
    	uni[0]=uni[len],uni[len+1]=uni[1];
    	for(int i=1;i<=len;++i) mp[uni[i]]=i;
    	for(int i=1;i<=n;++i)
    	{
    		bag[mp[a[i].l]].push_back(i);
    		bag[mp[a[i].r]].push_back(i);
    	}
    	int l=1,r=0;double L,R;bool flag=0;
    	for(;l<=len;++l)
    	{
    		while(cnt2<n&&r<len)
    		{
    			r++;
    			for(int i=0;i<(int)bag[r].size();++i)
    			sum[bag[r][i]]^=1,cnt1+=(sum[bag[r][i]])?1:-1,cnt2++;
    		}
    		if(cnt2==n&&cnt1==n)
    		{
    			L=uni[l]-0.1,R=uni[r]+0.1;
    			if(check(L)==check(R)) L=uni[l-1]+0.1;
    			if(check(L)==check(R)) R=uni[r+1]-0.1;
    			if(check(L)==check(R)) L=uni[l]-0.1;
    			if(check(L)!=check(R)){flag=true;break;}
    		}
    		for(int i=0;i<(int)bag[l].size();++i)
    		sum[bag[l][i]]^=1,cnt1+=(sum[bag[l][i]])?1:-1,cnt2--;
    	}
    	if(flag)
    	{
    		double xl,yl,xr,yr;
    		if(check(L)==1) xl=0,yl=L;
    		if(check(L)==2) xl=L-h,yl=h;
    		if(check(L)==3) xl=w,yl=h-(L-h-w);
    		if(check(L)==4) xl=w-(L-h-w-h),yl=0;
    		if(check(R)==1) xr=0,yr=R;
    		if(check(R)==2) xr=R-h,yr=h;
    		if(check(R)==3) xr=w,yr=h-(R-h-w);
    		if(check(R)==4) xr=w-(R-h-w-h),yr=0;
    		printf("1
    %.1lf %.1lf %.1lf %.1lf
    ",xl,yl,xr,yr);
    	}
    	else printf("2
    0 0.1 %.1lf %.1lf
    0 %.1lf %.1lf 0.1
    ",1.0*w,h-0.1,h-0.1,1.0*w);
    }
    
  • 相关阅读:
    Redis涉及的概念
    Redis高级教程
    Redis基础入门
    Java多线程面试题
    Java集合面试题
    Java集合基础
    Java基础面试题总结
    Zookeeper Basics
    GitLab基础入门
    阿里云ECS服务器Docker安装Tomcat过程记录
  • 原文地址:https://www.cnblogs.com/Point-King/p/13880520.html
Copyright © 2011-2022 走看看