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\times(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\n",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\n",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\nmid i\),所以不難得到\(2^k|i-2^k\),然后知道\(i-2^k\in[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\n",ans);
	}
}

完結撒花QAQ


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM