zoukankan      html  css  js  c++  java
  • 2020.2.6比赛题解

    T1 字串去重

    这题应该不用多说了,随便怎么做都可以。

    因为本题字符串已经排好序,可以直接判断这一个和上一个是否相等即可。

    也可以全部读入后用C++自带的(unique)函数去重。

    复杂度(O(Sigma len))

    标程:

    #include<bits/stdc++.h>
    using namespace std;
    string s[100010];
    int n;
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) cin>>s[i];
        n=unique(s+1,s+n+1)-s-1;
        for(int i=1;i<=n;i++) cout<<s[i]<<endl;
    }
    

    T2 十字连星

    这题不算难,我还降低了一定的难度,没有卡精度,以及卡常。

    这题分两类考虑,斜率的乘积等于-1,或者平行与坐标轴,相信大家都知道怎么求过两点的直线的斜率。我们令第(i)个点的坐标为((x_i,y_i)),则第过第(i,j)两点的直线的斜率为(frac{y_i-y_j}{x_i-x_j}),如果(y_i-y_j=0),则该直线平行于(x)轴,如果(x_i-x_j=0),则该直线平行于(y)轴。

    因为只有不超过(500)个点,所以只有不超过(frac{500 imes(500-1)}{2}=124750)条直线。如果暴力维护,则时间复杂度还是(O(n^4))

    考虑用一个数据结构来维护斜率,不难想到使用(STL)中的映射容器(map)来维护。每次加入一条线段前,统计增加的答案。

    复杂度(O(Tn^2log(n)))

    标程(1)(有精度误差):

    #include<bits/stdc++.h>
    using namespace std;
    struct node{int x,y;}p[510];
    map<double,int> mp; 
    int T,n;
    int main(){
        scanf("%d",&T);
        while(T--){
    	mp.clear();
            scanf("%d",&n);
            for(int i=1;i<=n;i++) scanf("%d %d",&p[i].x,&p[i].y);
            int cnt1=0,cnt2=0;
            long long ans=0;
            for(int i=1;i<=n;i++){
                for(int j=i+1;j<=n;j++){
                	if(p[i].x==p[j].x) cnt1++;
                	else if(p[i].y==p[j].y) cnt2++;
                	else{
                		double x=(p[i].x-p[j].x),y=(p[i].y-p[j].y);  		
                		mp[x/y]++;
                		ans+=mp[-y/x];
    				}
    			}
    		}
    		printf("%lld
    ",ans*4+(long long)cnt1*cnt2*4);
        }
    }
    

    标程(2)(无精度误差):

    #include<bits/stdc++.h>
    using namespace std;
    struct node{int x,y;}p[510];
    map<pair<int,int>,int> mp; 
    int T,n;
    int gcd(int x,int y){
        if(x%y==0) return y;
        return gcd(y,x%y);
    }
    int main(){
        scanf("%d",&T);
        while(T--){
    	mp.clear();
            scanf("%d",&n);
            for(int i=1;i<=n;i++) scanf("%d %d",&p[i].x,&p[i].y);
            int cnt1=0,cnt2=0;
            long long ans=0;
            for(int i=1;i<=n;i++){
                for(int j=i+1;j<=n;j++){
                	if(p[i].x==p[j].x) cnt1++;
                	else if(p[i].y==p[j].y) cnt2++;
                	else{
                		int x=(p[i].x-p[j].x),y=(p[i].y-p[j].y),gc=abs(gcd(x,y));
    			x/=gc;y/=gc;
    			if(x<0) x*=-1,y*=-1;            		
                		mp[make_pair(x,y)]++;
                		if(y<0) x*=-1,y*=-1;
                		ans+=mp[make_pair(y,-x)];
    		}
    	    }
    	}
    	printf("%lld
    ",ans*4+(long long)cnt1*cnt2*4);
        }
    }
    

    T3 迷宫寻路

    这题主要考察了大家对于广搜的理解。

    对于前面(40)分,直接深搜即可,(O(玄学))

    对于中间的(30)分,不难发现就是一个广搜的经典例题,直接上广搜,(O(nm))

    对于全部的数据,我们尝试改良广搜。广搜的思想就是维护一个优先队列,让现在距离小的位置放在前面,距离大的位置放在后面,在本题中队头和队尾的值差距不会超过1。

    现在取出队头,设队头的距离为(dis),考虑使用了传送门,设到达了((i,j))位置,则((i,j))的距离更新为(dis),因为原来队头就是最小的,所以直接放入队头即可。

    再考虑走到相邻的格子((i,j)),距离为(dis+1),因为队头和队尾的差不大于(1),所以放入队尾即可。

    总复杂度(O(nm))

    标程:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,k,dis[2010][2010],vis[2010][2010],nxt[5][2]={{},{0,1},{0,-1},{1,0},{-1,0}};
    char ch[2010][2010];
    pair<int,int> q[5000010];
    vector<pair<int,int> > v[2010][2010];
    int main(){
    	for(int i=0;i<=2005;i++){
    		for(int j=0;j<=2005;j++) ch[i][j]='#',dis[i][j]=2e9;
    	}
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++) scanf(" %c",&ch[i][j]);
    	}
    	scanf("%d",&k);
    	int x0,y0,x1,y1;
    	while(k--){
    		scanf("%d %d %d %d",&x0,&y0,&x1,&y1);
    		v[x0][y0].push_back(make_pair(x1,y1));
    	}
    	int l=1000000,r=1000000;
    	q[1000000]=make_pair(1,1);
    	dis[1][1]=0;
    	while(l<=r){
    		int x=q[l].first,y=q[l].second;
    		l++;
    		if(vis[x][y]) continue;
    		vis[x][y]=1;
    		for(int i=0;i<v[x][y].size();i++){
    			int nx=v[x][y][i].first,ny=v[x][y][i].second;
    			if(vis[nx][ny]) continue;
    			dis[nx][ny]=min(dis[nx][ny],dis[x][y]);
    			q[--l]=make_pair(nx,ny);
    		}
    		for(int i=1;i<=4;i++){
    			int nx=x+nxt[i][0],ny=y+nxt[i][1];
    			if(vis[nx][ny]||ch[nx][ny]=='#') continue;
    			dis[nx][ny]=min(dis[nx][ny],dis[x][y]+1);
    			q[++r]=make_pair(nx,ny);
    		}	
    	}
    	printf("%d",dis[n][m]==2e9?-1:dis[n][m]);
    }
    

    T4 整理家谱

    这题考到了一点关于位运算的分析。

    想一下(i&(i-1))代表了什么。我们以(10)为例子来试一下。(10&9=8)也就是((1010)_2&(1001)_2=(1000)_2),也就相当于去掉了二进制下的最后一位。

    现在考虑(i)最后一位所在的位置,设现在考虑从右往左数的第(k)位,则他的父亲为(i-2^{k-1})。我们可以知道(2^{k-1}|i),且(2^k mid i),所以不难得到(2^k|i-2^k),然后知道(i-2^kin[l,r]),所以直接用除法统计([l,r])之中的(2^k)的倍数即可,记得要分类考虑一下边界是否可以取到。

    复杂度(O(Tlog(值域))approx O(50T))

    标程:

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    long long l,r;
    int main(){
    	scanf("%d",&n);
    	while(n--){
    		scanf("%lld %lld",&l,&r);
    		long long ans=0;
    		for(int i=60;i>=1;i--){
    			long long nl=floor((double)(l-1)/(1LL<<i))+1,nr=r/(1LL<<i);
    			if(nl>nr) continue;
    			if((nr<<i)+(1LL<<(i-1))>r) ans+=nr-nl;
    			else ans+=nr-nl+1;
            }
    		printf("%lld
    ",ans);
    	}
    }
    

    完结撒花QAQ

  • 相关阅读:
    软件工程第二次作业
    软件工程第一次作业
    5T-时延小结
    4T--5G网络时延
    2T--网络切片
    1T--5G 三大应用场景
    2020软件工程第一次作业
    软件工程最后一次作业
    软件工程第四次作业
    软件工程第三次作业
  • 原文地址:https://www.cnblogs.com/Rimuru-Tempest/p/12270239.html
Copyright © 2011-2022 走看看