武汉科技大学第十一届程序设计校赛(周赛训练)

Posted WUTONGHUA02

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了武汉科技大学第十一届程序设计校赛(周赛训练)相关的知识,希望对你有一定的参考价值。

题解报告

题解顺序不是原来比赛的题目顺序
比赛链接

基本的一些理解和问题都在注释中
题目一:杰哥的最小和
如果A和B的最大公倍数为N,则有:

\\[N=A\\times B\\div GCD(A,B) \\]

可以将上述化为:

\\[N=(A\\div GCD(A,B))\\times B=>N=C\\times B \\]

如果\\(GCD(A,B)\\)不等于1的话,那么一定存在一个比A或B更小的C使得\\(GCD(C,B)\\)\\(GCD(A,C)\\)为1,而恰好这样得到的\\(B+C\\)或者\\(C+B\\)才最小,所以要让\\(GCD(A,B)\\)为1,才可以得到嘴下,那么可以将N进行质因数分解,然后将N的质因数分成两份集合,相同的质因数要再一个集合,然后枚举集合就行了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int gcd(int x,int y)

    return y?gcd(y,x%y):x;

int main(void)

    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    while(T--)
    
        int N;cin>>N;
        int res=N+1;
        for(int i=2;i<N/i;i++)//选择集合,直接枚举
            if(N%i==0&&gcd(i,N/i)==1)//这里的GCD(i,N/i)==1就代表没有相同的质因数再不同A和B中。
                res=min(res,i+N/i);
        cout<<res<<endl;
    
    return 0;

题目二:杰哥的树
进行状态压缩,然后统计不同状态个数后利用组合数学的知识求解就行了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=2e5+10;
int head[maxn],ver[maxn],nxt[maxn],edge[maxn],tot;
ll state[maxn];
void dfs(int now,int fa,int st)//从开头这里进行搜索。

    for(int i=head[now];~i;i=nxt[i])
    
        int v=edge[i];
        if(v==fa)continue;
        state[st^(1<<ver[i])]++;
        dfs(v,now,st^(1<<ver[i]));
    

void addEdge(int u,int v,int w)

    edge[tot]=v;
    ver[tot]=w-\'a\';
    nxt[tot]=head[u];
    head[u]=tot++;

int main(void)

    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    memset(head,-1,sizeof(head));
    int N;cin>>N;
    for(int i=0;i<N-1;i++)
    
        int u,v;char w;
        cin>>u>>v>>w;
        addEdge(u,v,(int)w);
        addEdge(v,u,(int)w);
    
    state[0]=1;
    dfs(1,0,0);
    ll res=0;
    for(int i=0;i<maxn;i++)res+=(state[i]*(state[i]-1))/2;
    cout<<res<<endl;
    return 0;

题目三:杰哥,你带我走吧!杰哥
预处理加树上差分就行了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int MOD=1e9+7;
const int maxn=2e5+10;
int edge[maxn],head[maxn],nxt[maxn],tot;
ll num[60][maxn];//前面为多少次方
ll sum[60][maxn];
int fa[maxn][40];//2的多少倍祖先
int deep[maxn];
int N,M;
void addEdge(int u,int v)

    edge[tot]=v;
    nxt[tot]=head[u];
    head[u]=tot++;

void dfs(int u,int ft,int dep)

    fa[u][0]=ft;
    deep[u]=dep;
    for(int i=2;i<=50;i++)num[i][u]=(num[i-1][u]*num[1][u])%MOD;
    for(int i=1;i<=50;i++)sum[i][u]=(num[i][u]+sum[i][ft])%MOD;
    for(int i=head[u];~i;i=nxt[i])
    
        int v=edge[i];
        if(v==ft)continue;
        dfs(v,u,dep+1);
    

void init()

    for(int i=1;i<35;i++)
        for(int j=1;j<=N;j++)
            fa[j][i]=fa[fa[j][i-1]][i-1];

int LCA(int u,int v)

    if(deep[u]<deep[v])swap(u,v);
    for(int i=30;i>=0;i--)
    
        if(deep[fa[u][i]]>=deep[v])
            u=fa[u][i];
    
    if(u==v)return v;
    for(int i=30;i>=0;i--)
    
        if(fa[u][i]!=fa[v][i])
        
            u=fa[u][i];
            v=fa[v][i];
        
    
    return fa[u][0];

int main(void)

    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    memset(head,-1,sizeof(head));
    cin>>N>>M;
    for(int i=1;i<=N;i++)cin>>num[1][i];
    for(int i=1;i<N;i++)
    
        int u,v;cin>>u>>v;
        addEdge(u,v);
        addEdge(v,u);
    
    dfs(1,0,1);
    init();
    for(int i=0;i<M;i++)
    
        int u,v,k;cin>>u>>v>>k;
        int ft=LCA(u,v);
        cout<<(sum[k][u]+sum[k][v]-sum[k][ft]-sum[k][fa[ft][0]]+2*MOD)%MOD<<endl;
    
    return 0;

题目四:杰哥的集合
并查集模拟,注意一下合成的方式就行了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <unordered_map>
using namespace std;
const int maxn=1e5+10;
vector<int> number[maxn];
unordered_map<int,int> nums[maxn];
int fa[maxn],MAX[maxn];
int find(int x)

    return fa[x]==x?x:fa[x]=find(fa[x]);

void merge(int x,int y)

    int fx=find(x);
    int fy=find(y);
    if(fx==fy)return;//注意这里要写,避免重复计算
    if(number[fy].size()<number[fx].size())swap(fx,fy);//这里要每次都是大的和小的,保证O(n)
    int len=number[fx].size();
    for(int i=0;i<len;i++)
    
        number[fy].push_back(number[fx][i]);
        nums[fy][number[fx][i]]++;
        if(nums[fy][number[fx][i]]>nums[fy][MAX[fy]]||(nums[fy][number[fx][i]]==nums[fy][MAX[fy]]&&number[fx][i]<MAX[fy]))MAX[fy]=number[fx][i];
    
    number[fx].clear();
    nums[fx].clear();
    fa[fx]=fy;

void init(int N)

    for(int i=1;i<=N;i++)fa[i]=i;

int main(void)

    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int N,M;cin>>N>>M;
    init(N);
    for(int i=1;i<=N;i++)
    
        int x;cin>>x;
        number[i].push_back(x);
        nums[i][x]++;MAX[i]=x;
    
    for(int i=0;i<M;i++)
    
        int OP;cin>>OP;
        if(OP==1)
            int x,y;cin>>x>>y;
            merge(x,y);
        else
            int x;cin>>x;
            cout<<MAX[find(x)]<<endl;
        
    
    return 0;

题目五:杰哥闯稻妻
BFS或者DFS搜索就行
注意一下状态压缩的方法

#include <cstdio>//由于题目限制了步数,且状态较少,所以使用bfs可以很快找到答案。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e4+10;
int End=3+(3<<2)+(3<<4)+(3<<6)+(3<<8);
struct node

    int st;
    string s;
state[maxn];
int vis[maxn];
int main(void)

    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    queue<node>q;
    int state=0;
    for(int i=0;i<5;i++)
    
        int x;cin>>x;
        state+=((x-1)<<(i*2));
    
    q.push(state,"");
    while(!q.empty())
    
        node now=q.front();q.pop();
        if(vis[now.st])continue;
        vis[now.st]=1;
        if(now.st==End)
        
            cout<<now.s.size()<<endl;
            int len=now.s.size();
            for(int i=0;i<len;i++)cout<<now.s[i]<<\' \';
            break;
        
        for(int i=0;i<5;i++)
        
            int Newstate=now.st;
            for(int j=-1;j<=1;j++)
            
                //这里的状态压缩记一下,方便以后直接进行压缩。
                int pos=(i+j+5)%5;//这里的括号要限制好,不要忘了。
                Newstate=((Newstate+(1<<(pos*2)))&(3<<(pos*2)))+(Newstate&(~(3<<(pos*2))));
            
            q.push(Newstate,now.s+(char)(i+\'1\'));
        
    
    return 0;

题目六:杰哥哄对象
注意状态的转移,当前状态可以由什么状态转移而来
注意一个字符可以进行的各种操作,或者不只是一个字符,一个状态可以由两个或多个字符的变换来确定。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1e7+10;
int dp[maxn];
int main(void)

    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    string s;cin>>s;
    int len=s.size();
    s="0"+s;dp[1]=1;
    for(int i=2;i<=len;i++)
    //每次以两个为判断,不牵扯别的完美字符串。
        dp[i]=dp[i-1]+1;//删去自己
        dp[i]=min(dp[i],dp[i-2]+(s[i]!=s[i-1]));//更换自己或者上一个
        if(i>=3&&s[i]==s[i-2])dp[i]=min(dp[i],dp[i-3]+1);//删去上一个
        //通过这三步操作一定是最小的,因为不可能一次删去两个,只能每次都这么选,删去两个需要的操作量较大。
    
    cout<<dp[len]<<endl;
    return 0;

题目七:杰哥抓气球
注意一下原点可能有气球就行

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int main(void)

    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    int ans=0;
    while(T--)
    
        int X,Y;cin>>X>>Y;
        if(!X&&!Y)continue;
        int Temp=X*X+Y*Y;
        int Int=(int)sqrt(Temp);
        if(Temp==Int*Int)ans+=2;
        else ans+=4;
    
    cout<<ans<<endl;
    return 0;

至于这场比赛其它的题目视乎都是我力所不能及的,就不补了

武汉大学校赛 tarjan+求最长链+拓扑排序

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
stack<int>s;
vector<int>vv[N];
vector<int>v2[N];
bool vis[N]; 
int n,m,tim,num,cnt,in_[N];int ans[N];
int dfn[N],low[N],bel[N],x,y,size[N],max_;queue<int>q;
void tarjan(int u)
{
    dfn[u]=low[u]=++tim;
    vis[u]=1;s.push(u);
    for(int i=0;i<vv[u].size();i++)
    {
        int v=vv[u][i];
        if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        int v;num++;
        do
        {
            v=s.top(),s.pop();
            vis[v]=0,bel[v]=num;
            size[num]++;
        }while(u!=v);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){scanf("%d%d",&x,&y);vv[x].push_back(y);}
    for(int i=1;i<=n;i++){
        sort(vv[i].begin(),vv[i].end());
        unique(vv[i].begin(),vv[i].end());
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
    for(int j=0;j<vv[i].size();j++){
        int u=bel[i],v=bel[vv[i][j]];
        if(u!=v) v2[u].push_back(v);
    }
    for(int i=1;i<=num;i++){
        sort(v2[i].begin(),v2[i].end());
        unique(v2[i].begin(),v2[i].end());
    }
    for(int i=1;i<=num;i++)
    for(int j=0;j<v2[i].size();j++){
        int u=i,v=v2[i][j];in_[v]++;
    }
    for(int i=1;i<=num;i++) if(in_[i]==0) q.push(i),ans[i]=size[i];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<v2[u].size();i++){
            int v=v2[u][i];
            ans[v]=max(ans[v],ans[u]+size[v]);in_[v]--;
            if(in_[v]==0) q.push(v);
        }
    }
    for(int i=1;i<=num;i++) max_=max(max_,ans[i]);
    printf("%d\n",max_);
    return 0;
}

 

以上是关于武汉科技大学第十一届程序设计校赛(周赛训练)的主要内容,如果未能解决你的问题,请参考以下文章

第一届河北工业大学程序设计竞赛校赛 个别题的解析

第一届佳木斯大学程序设计校赛题解

二数 (埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛)

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛 B合约数

西南民族大学第十届校赛(同步赛)

新年第一篇!西南民族大学第十届校赛(同步赛)