2021浙江省程序省赛(ACFGIJLM题解)

🎈A

签到,加起来就行了,记得等于属于先手赢(2A)

🎈C

题意

给八个点三维坐标,问是否在三维是立方体

思路

八个点的连成56条线,如果是立方体的话有8条,24条,24条相同的线,且都不相同

用map存ll,别开根号就行,有精度问题的(1A)

🎈F

题意

给你t个数据(1000个),两个数字n,m(1e8),n只能减,m只能加,问最小操作使得m%n==0

思路

m开根号暴力,然后要限制n的范围,我比赛没限制,血亏……

因为对称性就可以降复杂度m变成2 * sqrt(m)(赛后2A)

AC代码

#include <bits/stdc++.h>
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false)
#define inf 0x3f3f3f3f
using namespace std;
const int N=3e3+10;
int n,t,m;
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        int k=sqrt(m);
        int ans=2e8+10;
        for(int i=1;i<=k;i++){
            if(m%i==0){
                if(n>=i){
                    ans=min(ans,abs(n-i));
                }
                if(n>=m/i){
                    ans=min(ans,abs(m/i-n));
                }
            }
            else{
                if(n>=i){
                    ans=min(ans,abs(n-i)+abs(i-m%i));
                }
                if(n>=m/i+1){
                    ans=min(ans,abs(m/i+1-n)+abs(m/i+1-m%(m/i+1)));
                }
            }
        }
        printf("%d\n",ans);
    }
	return  0;
}
/*
1
31 92
*/

🎈G

题意

游戏中有一架用规则六边形拼成的飞机。这个平面上有蜂巢,蜂巢的方向是这样的:上面和下面都有六边形节点,左右两侧都有边缘,蜂巢与其所在行的相邻蜂巢共用。

随后的每一行相对于前一行移动半个蜂窝。轴沿水平蜂巢行从左到右。轴相对于轴倾斜60度。坐标轴在蜂巢处相交。

有攻击和查询两种操作。格莱美可以通过一次攻击行动征服一个蜂巢。

对于一个查询操作,格莱美想知道她是否在她征服的蜂巢和她没有征服的蜂巢之间筑起了墙,如果她从蜂巢出发在她的领地而不穿过任何墙,她能接触到多少墙

思路

因为5e5的范围,暴力连通块肯定tle,然后想到了并查集

用两个map分别标记占领的点(mp)和筑起的墙(vis)

然后每次查询如果这个点两个占领的点没标记过,遍历六个方向,看vis里面有没有墙,如果攻占过就直接输出并查集的父亲(参考ac代码的第二个样例)

每次攻击的点(开始给六面墙),遍历六个方向,如果遍历的点也是占领点的话,并起来,然后总数-2即可(因为双方都失去了一面墙)(比赛没做血亏,赛后1A)

AC代码

#include <bits/stdc++.h>
using namespace std;
struct node{
    int x,y;
    node(){}
    node(int xx,int yy):x(xx),y(yy){}
    friend bool operator<(const node a,const node b){
        if(a.x==b.x)return a.y<b.y;
        return a.x<b.x;
    }
};
const int N=5e5+10;
int f[N],g[N];
int F(int x){
    return f[x]==x?x:f[x]=F(f[x]);
}
int sx[6]={0,0,1,1,-1,-1};
int sy[6]={1,-1,0,-1,0,1};
int main(){
    int n;
    map<node,int>mp,vis;
    scanf("%d",&n);
    int cnt=1;
    for(int i=1;i<=n;i++){
        f[i]=i;g[i]=0;
    }
     for(int i=1;i<=n;i++){
        int x,y,c;
        scanf("%d%d%d",&c,&x,&y);
        if(c==2){
            int star=mp[node(x,y)];
            if(star==0){
                int ge=0;
                for(int j=0;j<6;j++){
                    int xx=x+sx[j],yy=y+sy[j];
                    if(vis[node(xx,yy)]){
                        ge++;
                    }
                }
                printf("%d\n",ge);
            }
            else{
                int u=F(star);
                printf("%d\n",g[u]);
            }
        }
        else{
            int star=mp[node(x,y)];
            if(star){
                continue;
            }
            else{
                mp[node(x,y)]=cnt;star=cnt++;
                if(vis[node(x,y)]){
                    vis[node(x,y)]=0;
                }
                g[star]=6;
                for(int j=0;j<6;j++){
                    int xx=x+sx[j],yy=y+sy[j];
                    int en=mp[node(xx,yy)];//cout<<en<<endl;
                    if(en){
                        int uu=F(star),vv=F(en);
                        if(uu!=vv){
                            f[uu]=vv;
                            g[vv]+=(g[uu]-2);//cout<<g[vv]<<endl;
                        }
                        else{
                            g[vv]-=2;
                        }
                    }
                    else{
                        vis[node(xx,yy)]=1;
                    }
                }
            }
        }
    }
    return 0;
}
/*
8
1 0 0
2 0 0
1 0 2
1 1 2
1 0 3
2 0 3
1 0 1
2 0 0
6
1 -1 2
1 2 0
1 2 -2
1 -1 -1
1 -2 1
2 0 0
*/

🎈I

题意

相交重叠的绳作三圈维恩图的分离判断问题,但不适合于边缘。

桌子上放着三根循环绳,它们的投影形成了维恩图。

如图所示,这些绳索(索引从1到3)有六个重叠的交点,分别索引从1到6。

想把绳子拉开,但看起来它们是绑在一起的,所以她需要用剪刀剪下三根绳子中的一部分。她想知道有多少种不同的方法来选择子集,以便这些绳子在之后是可分离的。你能告诉她答案吗?

注:两个子集是不同的,当且仅当其中一个子集中至少有一根绳子被选中,而另一个子集中没有被选中。

思路

(a[1] ^ a[4])+(a[2] ^ a[5])+(a[3] ^ a[6])

ans=3的时候是4种
ans=2的时候是5种
ans=1的时候是6种
ans=0的时候是8或7种

比赛没想到的圈

麻了麻了麻了麻了

AC代码

#include <bits/stdc++.h>
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false)
using namespace std;
int a[10];
int  main(){
	for(int i=0;i<6;i++){
        string c;cin>>c;
        if(c=="true"){
            a[i]=1;
        }
        else{
            a[i]=0;
        }
	}
	int ans=(a[0]^a[3])+(a[1]^a[4])+(a[2]^a[5]);
	int k=a[0]+a[1]+a[2]+a[3]+a[4]+a[5];
    if(k==2 && a[1] && ans==0){
        cout<<"7"<<endl;
	}
	else if(k==4 && a[0] && a[2] && ans==0){
        cout<<"7"<<endl;
	}
	else if(ans==0){
        cout<<"8"<<endl;
	}
	else if(ans==1){
        cout<<"6"<<endl;
	}
	else if(ans==2){
        cout<<"5"<<endl;
	}
	else{
        cout<<"4"<<endl;
	}
	return  0;
}
/*
0 1 0 0 1 0
1 0 1 1 0 1
都是7种
*/

🎈J

题意

给你一个n点,m条边的无向图,2~n点上有珠宝,每个都有价值ai。从点1开始。穿过每一个边缘消耗1个单位时间。她可以在顶点捡起一块珠宝,然后在点1放下。捡起和放下一件珠宝可以立即完成。

此外,她在任何时候最多可以携带1件珠宝。

当她放下一件按顶点估价的珠宝时,她得到了它的价值。

现在,对于每一个时间单位,她想知道她能得到的最大值是多少。

思路

bfs找1点到每个点的最短距离,然后多重背包即可(9A)

AC代码

先咕了,明天再打

#include <bits/stdc++.h>
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false)
#define inf 0x3f3f3f3f
using namespace std;
const int N=3e3+10;
int ne[N<<1],to[N<<1],head[N];
int vis[N];
int n,m,t;
int dp[N],bu[N];
int a[N],ans[N];
int tot=0;
void add(int u,int v){
    ne[tot]=head[u];
    to[tot]=v;
    head[u]=tot++;
}
struct node{
    int x,y;
    node(){}
    node(int xx,int yy):x(xx),y(yy){}
    friend bool operator<(const node a,const node b){
        return a.y>b.y;
    }
};
void bfs(){
    priority_queue<node>q;
    q.push(node(1,0));
    vis[1]=1;
    while(!q.empty()){
        node k=q.top();q.pop();
        int u=k.x,b=k.y;
        for(int i=head[u];~i;i=ne[i]){
            int v=to[i];
            if(!vis[v]){
                q.push(node(v,b+1));
                bu[v]=(b+1)*2;
                vis[v]=1;
            }
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&t);
    for(int i=2;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=0;i<=n;i++){
        vis[i]=0;head[i]=-1;dp[i]=0;bu[i]=inf;ans[i]=0;
    }
    for(int i=0;i<m;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    bfs();
    for(int i=2;i<=n;i++){
        for(int j=bu[i];j<=t;j++){
            ans[j]=max(ans[j],ans[j-bu[i]]+a[i]);
        }
    }
    for(int i=1;i<=t;i++){
        printf(i==t?"%d\n":"%d ",ans[i]);
    }
	return  0;
}
/*
5 6 5
2 3 4 5
1 2
4 5
5 5
2 3
1 3
3 3
*/

🎈L

思路

没读过,队友告诉我笼统的写法,我kmp循环节直接过了(2A)

AC代码

#include <bits/stdc++.h>
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false)
using namespace std;
const int N=1e5+10;
int ne[N];
char s[N];
void GetNext(){
    int l=strlen(s);
    int i=0;int j=-1;
    ne[0]=-1;
    while(i<l){
        if(j==-1 || s[i]==s[j]){
            i++;
            j++;
            ne[i] = j;
        }
        else
            j = ne[j];
    }
    return;
}
int main(){
    int f=1;
    int n;
    scanf("%d",&n);
    scanf("%s",s);
    GetNext();
    for(int i=1;i<=n;i++){
        if(i-ne[i]!=i){
            f=0;break;
        }
    }
    if(f){
        printf("Correct\n");
    }
    else{
        printf("Wrong Answer\n");
    }
    return  0;
}
/*
*/

🎈M

题意

问每个同学可以选择1~20之内的数,如果别人的数大于自己的获得10分,不然扣10分,相等不扣分

问最高能获得分数的概率

思路

10分钟看完题面,贪逼队友说全是20,不是就是0.0000嘛,于是交了一发。(1A)

把能写的题写完,看I题题解看不懂,告辞

posted @ 2021-04-20 19:48  ouluy  阅读(775)  评论(1编辑  收藏  举报