2020.2.6比赛题解

T1 字串去重

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

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

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

复杂度\(O(\Sigma len)\)

标程:

#include
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<

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
using namespace std;
struct node{int x,y;}p[510];
map 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
using namespace std;
struct node{int x,y;}p[510];
map,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
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 q[5000010];
vector > 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

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
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<nr) continue;
            if((nr<r) ans+=nr-nl;
            else ans+=nr-nl+1;
        }
        printf("%lld\n",ans);
    }
}

完结撒花QAQ

你可能感兴趣的:(2020.2.6比赛题解)