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

题解报告

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

基本的一些理解和问题都在注释中
题目一:杰哥的最小和
如果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;
}

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

posted @ 2023-04-03 05:26  WUTONGHUA02  阅读(15)  评论(0编辑  收藏  举报