zoukankan      html  css  js  c++  java
  • 2021ICPC亚洲区域赛(昆明)复盘

    2021ICPC亚洲区域赛(昆明)复盘

    Wogua_boy

    I.Mr. Main and Windmills(计算几何)

    题意:

    Mr.Main坐火车从s到t,经过了许多风车。

    火车在一条直线上行驶。

    随着火车的行驶,风车在Mr.Main的视野里会发生位置相对变化。

    现在给出风车们的坐标,请你找到当第h个风车与其他风车的相对位置变化k次时火车所在的坐标。

    题解:

    观察后发现,只需要取风车坐标两两之间直线和s到t线段的交点然后排序就好了。

    比赛的时候因为天生对计算几何的恐惧直接扔给队友了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1010;
    const double eps=1e-8;
    int n,m;
    double xs,ys,xt,yt; 
    int sgn (double x) {
    	if (fabs(x)<eps) return 0;
    	else if (x<0) return -1;
    	else return 1;
    }
    double x[maxn],y[maxn];
    vector<pair<double,double> > p[maxn];
    //每个点和剩下所有点连成的直线与母线的交点
    pair<double,double> jd (double x1,double y1,double x2,double y2,double x3,double y3,double x4,double y4) {
    	if (x1==x2&&y1==y2) return make_pair(1e18,1e18);
    	//(x1,y1),(x2,y2)组成的直线和(x3,y3),(x4,y4)组成的直线的交点
    	double k1=(x1==x2?1e18:(y1-y2)/(x1-x2));
    	double k2=(y1==y2?1e18:(y3-y4)/(x3-x4));
    	if (sgn(k1-k2)==0) return make_pair(1e18,1e18);
    	//k1是1e18,k2不是,答案就是x1*k2+b2
    	if (k1==1e18) {
    		return make_pair(x1,x1*k2+y3-k2*x3);
    	} 
    	else if (k2==1e18) {
    		return make_pair(x2,x2*k1+y1-k1*x1);
    	}
    	double b1=y1-k1*x1;
    	double b2=y3-k2*x3;
    	double x=(b2-b1)/(k1-k2);
    	double y=k1*x+b1;
    	return make_pair(x,y);
    }
    int cmp (pair<double,double> x,pair<double,double> y) {
    	if (sgn(x.first-y.first)!=0) {
    		return sgn(x.first-y.first)<0;
    	}
    	else {
    		return sgn(x.second-y.second)<0;
    	}
    } 
    int main () {
    	scanf("%d%d",&n,&m);
     	scanf("%lf%lf%lf%lf",&xs,&ys,&xt,&yt);
     	for (int i=1;i<=n;i++) scanf("%lf%lf",x+i,y+i);
     	for (int i=1;i<=n;i++) {
     		for (int j=1;j<=n;j++) {
     			if (i==j) continue;
     			pair<double,double> it=jd(x[i],y[i],x[j],y[j],xs,ys,xt,yt);	
     			if (it.first==1e18) continue;//没有交点
    			if (sgn(it.first-min(xs,xt))<0||sgn(it.first-max(xs,xt))>0||sgn(it.second-min(ys,yt))<0||sgn(it.second-max(ys,yt))>0) continue;//交点在线段以外
    			p[i].push_back(it); 
    		}
    		if (xs<xt) sort(p[i].begin(),p[i].end(),cmp);
    		else if (xs>xt) sort(p[i].rbegin(),p[i].rend(),cmp);
    		else if (ys<yt) sort(p[i].begin(),p[i].end(),cmp);
    		else sort(p[i].rbegin(),p[i].rend(),cmp);
    	}
    	while (m--) {
    		int h,t;
    		scanf("%d%d",&h,&t);
    		//printf("%d
    ",p[h].size());
    		if (t>p[h].size()) {
    			printf("-1
    ");
    			continue;
    		}
    		printf("%.10f %.10f
    ",p[h][t-1].first,p[h][t-1].second);
    	}
    }
    
    

    J.Parallel Sort(构造)

    题意:

    给出一个数组,单轮可以交换任意两个元素的值,但是同一个下标在一轮中只能调用一次。

    询问至少几轮可以使数组有序?

    题解:

    比赛的时候在错误的贪心思路陷进去了。觉得(10^5)的数据范围,(nlogn)复杂度很对。但实际上只需要两次即可。

    考虑这样一组样例:

    6
    2 3 4 5 6 1
    

    我们将元素应该在的位置和它当前位置连边,可以得到这样的图:

    image-20210404145636097

    一个显然的贪心思路是2和3连边,4和5连边,6和1连边,然后第二轮继续这个贪心方法,即遇到能换的就换,扩展之后可以发现大概需要(logn)次可以完成排序的任务。

    但是有一个更加优秀的构造方法。观察之后可以发现,任意排列通过图表示,都是一个一个独立的环。

    对于这个环,第一轮分别交换[1,2],[3,6],[4,5],这样可以得到一个这样的数组:

    6
    2 3 4 5 6 1
    1 6 5 4 3 2
    

    这样可以把这个环拆成若干个小环,比如这样:

    image-20210404150146840

    进一步观察后可以发现,每个环都可以用这种方法拆成若干个长度为2的小环。

    第二轮对每个环交换一次即可。

    所以稳定小于等于2次。

    做法就是先处理出每个环的信息,然后将每个环的第一个元素和倒数第一个元素、第二个元素和倒数第二个元素...交换,可以把环拆成若干个大小小于等于2的小环。

    第二轮就一步到位了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+100;
    int n,p[maxn];
    int vis[maxn];
    vector<pair<int,int> > ans[2];
    int b[maxn];
    int main () {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++) scanf("%d",p+i),b[p[i]]=i;
    	for (int i=1;i<=n;i++) {
    		if (p[i]==i) continue;
    		if (vis[p[i]]) continue;
    		int u=p[i];
    		vector<int> tt;
    		while (p[u]!=p[i]) {
    			tt.push_back(b[u]);
    			u=p[u];
    		}
    		for (int j=0;j<tt.size()/2;j++) {
    			swap(p[tt[j]],p[tt[tt.size()-j-1]]);
    			b[p[tt[j]]]=tt[j];
    			b[p[tt[tt.size()-j-1]]]=tt[tt.size()-j-1];
    			ans[0].push_back(make_pair(tt[j],tt[tt.size()-j-1]));
    		}
    	}
    	int f=1;
    	for (int i=1;i<=n;i++) if (p[i]!=i) f=0;
    	if (f) {
    		if (ans[0].size()==0) return printf("0"),0;
    		printf("1
    ");
    		printf("%d",ans[0].size());
    		for (pair<int,int> i:ans[0]) printf(" %d %d",i.first,i.second);
    		printf("
    ");
    		return 0;
    	}
    	for (int i=1;i<=n;i++) {
    		if (p[i]==i) continue;
    		ans[1].push_back(make_pair(i,b[i]));
    		swap(p[i],p[b[i]]);
    		b[p[i]]=i;
    		b[p[b[i]]]=b[i];
    	}
    	int cnt=((ans[0].size()>0?1:0)+(ans[1].size()>0?1:0)); 
    	printf("%d
    ",cnt);
    	for (int i=0;i<2;i++) {
    		if (!ans[i].size()) continue; 
    		printf("%d",ans[i].size());
    		for (pair<int,int> j:ans[i]) printf(" %d %d",j.first,j.second);
    		printf("
    ");
    	}
    }
    

    M.Stone Games(结论推导+可持久化线段树维护)

    题意:

    给出一个数组,每次询问一个区间,请你找到最小的x,使得这个区间里的数通过加法凑不出x。

    强制在线。

    题解:

    这道题应该是2019ICPC徐州的弱化版本。

    首先,没有1的话答案就是1。

    然后2的话,求小于2的所有数的和,设这个和为sum,如果sum<2,那么答案就是2,否则[1~sum]都可以被表示出来。

    再去找比sum+1小的数的和,如果这个和小于sum+1,说明答案就是sum+1,否则找到sum+1继续相同的操作。

    那么做法就是:先对所有的数离散化,建可持久化线段树。

    然后,用可持久化线段树维护区间比某个数小的数的和即可。这个和是每次近似两倍的速度增长的,时间复杂度大概是(O(32nlogn))

    ICPC昆明M题结论的证明:

    假设当前维护的集合(S)可以构造出1到x的所有数,同时和为x。一开始(S)为空。

    现在想看看是否能构造出x+1这个数。

    首先,比x+1大的数是不可能对构造产生贡献的。所以先找所有值为x+1的数。假设一共有y个值为x+1的数,当前(sum=y(x+1)+x=(y+1)(x+1)-1)

    现在尝试构造一个数k,(x+1 leq k leq sum)。根据上面的柿子,(k/(x+1))一定小于等于(y),那么就可以这样构造:

    (t_1=k/(x+1),t_2=k\%(x+1))

    (t_1 leq y),所以一定存在足够的值为(x+1)的数来构造(t_1)

    (t_2 leq x),之前的集合(S)并没有被使用过,直接用集合(S)来构造(t_2)即可。

    因此当前集合一定可以构造出(1)(sum)的每个数。

    然后继续看是否能构造出(sum+1)这个数。

    依次类推。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+100;
    const int M=maxn*40;
    int a[maxn],t[maxn];
    int T[maxn];
    int lson[M];
    int rson[M];
    ll c[M];
    int tot;
    int n,m,q;
    int build (int l,int r) {
    	int root=tot++;
    	c[root]=0;
    	if (l!=r) {
    		int mid=(l+r)>>1;
    		lson[root]=build(l,mid);
    		rson[root]=build(mid+1,r);
    	}
    	return root;
    }
    int up (int root,int p,int v) {
    	int newRoot=tot++;
    	int tmp=newRoot;
    	int l=1,r=m;
    	c[newRoot]=c[root]+t[p];
    	while (l<r) {
    		int mid=(l+r)>>1;
    		if (p<=mid) {
    			lson[newRoot]=tot++;
    			rson[newRoot]=rson[root];
    			newRoot=lson[newRoot];
    			root=lson[root];
    			r=mid;
    		}
    		else {
    			rson[newRoot]=tot++;
    			lson[newRoot]=lson[root];
    			newRoot=rson[newRoot];
    			root=rson[root];
    			l=mid+1;
    		}
    		c[newRoot]=c[root]+t[p];
    	}
    	return tmp;
    }
    ll query (int left_root,int right_root,int l,int r,int L,int R) {
    	if (L>R) return 0;
    	if (l>=L&&r<=R) return c[left_root]-c[right_root];
    	int mid=(l+r)>>1;
    	ll ans=0;
    	if (L<=mid) ans+=query(lson[left_root],lson[right_root],l,mid,L,R);
    	if (R>mid) ans+=query(rson[left_root],rson[right_root],mid+1,r,L,R);
    	return ans;
    }
    int main () {
    	scanf("%d%d",&n,&q);
    	for (int i=1;i<=n;i++) scanf("%d",a+i),t[i]=a[i];
    	sort(t+1,t+n+1);
    	m=unique(t+1,t+n+1)-t-1;
    	for (int i=1;i<=n;i++) a[i]=upper_bound(t+1,t+m+1,a[i])-t-1;
    	ll ans=0;
    	T[n+1]=build(1,m);
    	for (int i=n;i>=1;i--) T[i]=up(T[i+1],a[i],1);
    	while (q--) {
    		ll l,r;
    		scanf("%lld%lld",&l,&r);
    		ll ql=min((l+ans)%n+1,(r+ans)%n+1);
    		ll qr=max((l+ans)%n+1,(r+ans)%n+1);
    		//printf("%lld %lld
    ",ql,qr);
    		ll tt=1;
    		while (1) {
    			ll t1=-1;
    			int L=1,R=m;
    			while (L<=R) {
    				int mid=(L+R)>>1;
    				if (t[mid]<=tt) {
    					t1=mid;
    					L=mid+1;
    				}
    				else{
    					R=mid-1;
    				}
    			}
    			ll t2=query(T[ql],T[qr+1],1,m,1,t1);
    			if (t2<tt) {
    				ans=tt;
    				break;
    			}
    			tt=t2+1;
    		}
    		printf("%lld
    ",ans);
    	}
    }
    
    
  • 相关阅读:
    在django中,执行原始sql语句
    Django的select_related 和 prefetch_related 函数优化查询
    Django -- settings 详解
    Django-models的字段类型
    ORM
    django-response对象
    spark集群进入 bin 下面目录./spark-shell 出现Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
    Hbase启动的时候出现:[RpcServer.handler=28,port=60000] ipc.RpcServer: RpcServer.handler=28,port=60000: exiting,master.HMasterCommandLine: Master exiting
    c++实现写一个函数,求2个整数的和,要求在函数体内不得使用+,-* /
    nutch二次开发环境搭建
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14616364.html
Copyright © 2011-2022 走看看