北京化工大学2022-2023-1 ACM集训队每周程序设计竞赛(10)题解

北大没做北化校赛血亏 

离世界第一就差一个北化

目录

问题 A: 坤坤大闹天宫1

问题 B: 坤坤大闹天宫2

问题 C: 坤坤大闹天宫3

问题 D: 坤坤大闹天宫4

问题 E: 坤坤大闹天宫5

问题 F: 坤坤大闹天宫6


 

问题 A: 坤坤大闹天宫1

难度指数:⭐⭐

小思维

因为X落在10^{9}

我们设

f(n)=n^{5}-(n-1)^{5}

n大于120时,f(n)就大于10^{9}了,所以答案一定在[-120,120]

直接跑一遍二重循环即可

代码如下

typedef long long ll;
 
int main()
{
    ll x;
    cin>>x;
    for(ll i=-120;i<=120;i++){
        for(ll j=-120;j<120;j++)
            if(i*i*i*i*i-j*j*j*j*j==x){
                cout<<i<<" "<<j;
                return 0;
            }
    }
    return 0;
}

问题 B: 坤坤大闹天宫2

难度指数:⭐

直接按题目意思模拟即可

代码如下

 
int a[N];
int main()
{
    int n,k,t;
    cin>>n>>k;
    while(k--){
        cin>>t;
        while(t--){
            int kk;
            cin>>kk;
            a[kk]++;
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(a[i]==0) ans++;
    }
    cout<<ans<<endl;
    return 0;
}

问题 C: 坤坤大闹天宫3

难度指数:⭐⭐

因为n很小,只有12

所以这是一道经典的搜索问题

爆搜即可

代码如下

const int N=15;
typedef long long ll; 
 
int n,m,x;
int cost[N];
int w[N][N];
int v[N];
int mincost=0x3f3f3f3f;
int sumcost=0;
 
void dfs(int u)
{
	if(u==n) return;
	
	sumcost+=cost[u];//选择这本书u 
	for(int i=0;i<m;i++) v[i]+=w[u][i];
	bool flag=1;
	for(int i=0;i<m;i++) if(v[i]<x) flag=0;
	if(flag)
		if(sumcost<mincost) mincost=sumcost;
	dfs(u+1);//进入下一本书的选择 
	sumcost-=cost[u];
	for(int i=0;i<m;i++) v[i]-=w[u][i]; 
	
	dfs(u+1);//不选这本书u,进入下一本书的选择 
}
 
int main()
{
	cin>>n>>m>>x;
	for(int i=0;i<n;i++)
	{
		cin>>cost[i];
		for(int j=0;j<m;j++) cin>>w[i][j];
	}
	dfs(0);
	if(mincost==0x3f3f3f3f) cout<<"-1";
	else cout<<mincost;
 
	return 0;
}

问题 D: 坤坤大闹天宫4

难度指数:⭐⭐⭐⭐

这道题想法可以有很多

这里说其中一种

隔板法:n个球,就有n+1个隔板,除了最两边的隔板外,每删除一个隔板,相当于把相邻的球弄为一种颜色,最多有k个相邻,说明最多可以删除k个隔板,然后同一个隔板内部的可以视为一个整体,相当于一种颜色,第一个隔板内部有m个选择,第二个有m-1个选择,第三个也有m-1个选择,直到最后一个,所以公式就是

\sum_{i=0}^{k}C_{n-1}^{i}*(m-1)^{(n-1-i)}*m

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 10;
const ll mod = 998244353;
ll fact[N], infact[N];
ll n, m, k;
ll qmi(ll a, ll k, ll p) {
    int res = 1;
    while (k) {
        if (k & 1) res = (ll)res * a % p;
        a = (ll)a * a % p;
        k >>= 1;
    }
    return res;
}
void init() {
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i++) {
        fact[i] = (ll)fact[i - 1] * i % mod;
        infact[i] = (ll)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
}
int main() {
    init();
    cin >> n >> m >> k;
    ll res = 0;
    for (int i = 0; i <= k; i++) {
        int a = n - 1, b = i;
        res = (ll)res + m * fact[a] % mod * infact[b] % mod * infact[a - b] %mod * qmi(m - 1, n - 1 - i, mod) % mod;
        res = res % mod;
    }
    cout << res << endl;
}

问题 E: 坤坤大闹天宫5

难度指数:⭐⭐⭐

贪心,判断括号匹配的字符串问题
首先给出的所有字符串的左右括号数是要匹配的
接下来判断这些字符串能否构成合理的括号序列,可以用pair存储每个字符串需要的匹配数和需要的右括号数

我们试想,合法的括号序列一定是左括号尽量靠左,右括号靠右的,所以我们要对其就行排序,最后用一个变量记录当前的待匹配数res,如果pair 的右括号数超过待匹配数,则输出No

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int M = 1e5+7;
char s[M];
struct na{
    int x,y;
    bool operator <(const na &r)const
    {
        return y<r.y;
    }
};
struct nb{
    int x,y;
    bool operator <(const nb &r)const
    {
        return x>r.x;
    }
};
vector<na>va;
vector<nb>vb;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        int l=strlen(s);
        int a=0,b=0;
        for(int j=0;j<l;j++)
        {
            if(s[j]=='(')a++;
            else
            {
                if(a)a--;
                else b++;
            }
        }
        //这里a的意思是需要在这个字符串后面补多少右括号,b是需要在其前面补多少左括号 
        //当a>b时,说明做左括号比右括号多,肯定优先放前面。显然,此时b越小在前面越优,因为需要在前面补左括号的个数就少了 
        //a<b时同理
    //  cout<<a<<" "<<b<<endl;
        if(a>=b)va.pb(na{a,b});
        else vb.pb(nb{a,b});
    } 
    sort(va.begin(),va.end());
    sort(vb.begin(),vb.end());
    bool f=true;
    int sm=0;
    for(int i=0;i<va.size();i++)
    {
        sm-=va[i].y;
        if(sm<0)f=false;//前面的左括号能否满足当前字符串的需要
        sm+=va[i].x;//当前字符串所能提供的左括号
        //肯定是左括号需求量少的在前面 
    }
    for(int i=0;i<vb.size();i++)
    {
        sm-=vb[i].y;
        if(sm<0)f=false;
        sm+=vb[i].x;
        //右边肯定是对称的来考虑,即需求后面右括号少的放在最右边 
    }
    if(sm!=0)f=false;
    if(f)cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}

问题 F: 坤坤大闹天宫6

难度指数:⭐⭐⭐⭐

首先给出结论:后手必胜当且仅当这棵树有完美匹配

(完美匹配:我们定义一组节点是两个节点且这两个节点间连一条边,如果一棵树可以分成若干个这样的组,那么说这棵树拥有完美匹配)

这棵树有完美匹配的时候,因为先手染什么颜色后手只要染它的匹配,就能保证每个白色节点有至少一个与它相邻的黑色节点了

如果一个点连着两个以上的叶子必定是先手必胜了,所以接下来我们默认所有的点连着的叶子个数小于等于1

如果没有完美匹配,我们随便找一个点为根,然后对于一个叶子,先手把它的父亲染白,那么后手必须把这个叶子染黑。然后我们把这两个点从树中删去,继续操作。显然树不会被删完,否则就存在完美匹配了。此时我们随便染白一个还未染的点就赢了

于是充要性都有了

求完美匹配直接从叶子开始贪心

代码如下

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define fer(i,a,b) for(int i=a;i<=b;++i)
#define der(i,a,b) for(int i=a;i>=b;--i)
#define all(x) (x).begin(),(x).end()
#define pll pair<int,int>
#define et  cout<<'\n'
#define xx first
#define yy second
using namespace std;
template <typename _Tp>void input(_Tp &x){
    char ch(getchar());bool f(false);while(!isdigit(ch))f|=ch==45,ch=getchar();
    x=ch&15,ch=getchar();while(isdigit(ch))x=x*10+(ch&15),ch=getchar();
    if(f)x=-x;
}
template <typename _Tp,typename... Args>void input(_Tp &t,Args &...args){input(t);input(args...);}
const int N=1e6+10;
vector<int> v[N];
int vis[N];
void dfs(int son,int fa){
    for(auto t:v[son]){
        if(t==fa) continue;
        dfs(t,son);
    }
    if(!vis[son]&&vis[fa]){
        puts("First");
        exit(0);
    }
    if(!vis[son]){
        vis[son]=vis[fa]=1;
    }
}
signed main()
{
    int n;
    cin>>n;
    fer(i,1,n-1){
        int a,b;
        cin>>a>>b;
        v[a].pb(b);
        v[b].pb(a);
    }
    vis[0]=1;
    dfs(1,0);
    puts("Second");

}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值