牛客练习赛128(ABCD、思维、01背包、树上DP、并查集)

牛客练习赛128(ABCD、思维、01背包、树上DP、并查集)


非数论选手,直接放弃最后两题


A. Cidoai的幂次序列(思维)

考虑特殊情况:1x = 1。这时,如果可以凑出一个 n-1 即可,显然 (n-1){1%2} = n-1。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int main(){
    
    ll n, k;
    cin >> n >> k;
    
    cout << 2 << endl;
    cout << n-1 << " " << 1 << endl;
    
    return 0;
}

B. Cidoai的平均数对(01背包)

第一想法,DP,算时间复杂度,O(5004),超时,需要优化。

考虑平均数约束的作用。首先,bi 不大于 k 的物品,全部选取。这时对于已经被选择的物品,记 x = ∑ ( k − b i ) x = \sum(k - b_i) x=(kbi),可以把问题转化为一个容量为 x 的01背包问题。

#include<bits/stdc++.h>

using namespace std;

const int maxn = 505;

int a[maxn], b[maxn];
int dp[maxn * maxn];

int main(){
    
	int n, k;
	cin >> n >> k;
	for(int i = 1; i <= n; i++) cin >> a[i] >> b[i];
	for(int i = 1; i <= n; i++) b[i] -= k;
	
	int sum_a = 0, sum_b = 0;
	for(int i = 1; i <= n; i++){
		if(b[i] < 0){
			sum_a += a[i];
			sum_b += -1 * b[i]; // 富裕的体积收集起来
		}
	}
	
	for(int i = 1; i <= n; i++){    // 01背包往富裕的体积里装
		if(b[i] < 0) continue;
		for(int j = sum_b; j >= b[i]; j--){
			dp[j] = max(dp[j], dp[j-b[i]] + a[i]);
		}
	}
	
	cout << sum_a + dp[sum_b] << endl;
    
    return 0;
}

C. Cidoai的树上方案(树上DP)

典中典。

记引入的新点为 now。

首先,在树上没有环。如果新图中出现了三元环,只能是由连接now的边构成了三元环。

如果一点连接 now,其儿子不可连接now,否则会构成三元环。

对于树上任意点x, D P [ x ] [ 0 ] = DP[x][0] = DP[x][0]= 点 x 不连接now时贡献的方案数, D P [ x ] [ 1 ] = DP[x][1] = DP[x][1]= 点 x 连接now时贡献的方案数。

对于任意一个非叶节点 x :

  • D P [ x ] [ 0 ] = ∏ ( D P [ y ] [ 0 ] + D P [ y ] [ 1 ] ) , y ∈ ( x 的孩子节点 ) DP[x][0] = \prod(DP[y][0] + DP[y][1]),y\in(x的孩子节点) DP[x][0]=(DP[y][0]+DP[y][1])y(x的孩子节点)。x下的各个孩子互不影响,故而为累乘。
  • D P [ x ] [ 1 ] = ∏ ( D P [ y ] [ 0 ] ) , y ∈ ( x 的孩子节点 ) DP[x][1] = \prod(DP[y][0]),y\in(x的孩子节点) DP[x][1]=(DP[y][0])y(x的孩子节点)

对于任意叶节点x:

  • D P [ x ] [ 0 ] = D P [ x ] [ 1 ] = 1 DP[x][0] = DP[x][1] = 1 DP[x][0]=DP[x][1]=1
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const ll mod = 998244353;
const int maxn = 2e6 + 5;

vector<int> mp[maxn];
ll v[maxn][2];

ll dfs(int root, int flag){ // flag表示root是否连接x
    if(v[root][flag]) return v[root][flag];	// 记忆化搜索,否则会超时
    if(mp[root].size() == 0) return v[root][flag] = 1;
    ll res = 1;
    if(flag == 1){ // root链接x
        for(auto son : mp[root]) 
            res = res * dfs(son, 0) % mod;
    }
    else{    // root没链接x
        for(auto son : mp[root]) 
            res = res * (dfs(son, 0) + dfs(son, 1)) % mod;
    }
    return v[root][flag] = res;
}


int main(){
    
    int n;
    cin >> n;
    for(int i = 2; i <= n; i++){
        int x;
        cin >> x;
        mp[x].push_back(i);
    }
    
    ll res = (dfs(1, 0) + dfs(1, 1)) % mod;
    
    cout << res << endl;
    
    return 0;
}

D. Cidoai的字符集合(并查集)

根据题意,把存在相似的歌曲放入一个set,其他歌曲每个单独成为一个set。

存在单词相同的歌曲连接一条边,使用并查集维护set和set的大小。

#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
using namespace std;

const int maxn = 1e6 + 5;

map<string, int> ID;
int ids = 0;
int get_id(string s){	// 给每个单词一个id,方便后续使用
	if(ID[s] == 0) ID[s] = ++ids;
	return ID[s];
}

set<int> mp[maxn];		// 记录每个旋律里有什么歌 
int f[maxn], sz[maxn];	// 并查集用 

int find(int x){	
	if(f[x] == x) return x;
	else return f[x] = find(f[x]);
}

void join(int x, int y){
	int fx = find(x);
	int fy = find(y);
	
	if(fx != fy){
		f[fx] = fy;
        sz[fy] += sz[fx];
	}
}

int main(){
    
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++){
		int k;
		cin >> k;
		string s;
		for(int j = 1; j <= k; j++){
			cin >> s;
			int id = get_id(s); 
			mp[id].insert(i);
		}
	}
	
	for(int i = 1; i <= n; i++) f[i] = i, sz[i] = 1;
	for(int i = 1; i <= ids; i++){
		int u = -1, v = -1;
		for(auto x : mp[i]){
			if(u == -1) u = x; // 该旋律下的第一首歌 
			else{
				v = x;
				join(u, v);	// 放入到和第一首歌相同的集合里 
			}
		}
	}
	int res = 0, flag = 0;
	for(int i = 1; i <= n; i++){
        if(find(i) == i){ // 是根节点 
            if(sz[i] > 1) flag = 1;
            else res++;
        }
    } 
	cout << res + flag << endl;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值