CF1229A Marcin and Training Camp

题意

有n个学生。每个学生有2个属性值 a_i 和 b_i。b_i 代表每个学生的能力值(越高越好).a_i 的定义如下:如果第i个学生知道第j个算法,则 a_i的二进制表示的第j位设为1,否则设为0.

当且仅当学生x知道学生y不知道的某种算法时,学生x认为自己比学生y更强(因此,有可能两个学生都认为自己比对方更强)。如果一个小组中没有一个学生认为自己比其他所有人都强,那么这个小组的人才能合作。

现在需要派出至少两个学生组成的小组的同时最大化小组中所有同学的能力值之和。求这个最大的能力值之和。

输入

第一行一个整数n 代表学生总人数。
第二行n个整数a_i 第三行n个整数b_i 含义见题意。
1 <= n <= 7000 0 <= a_i < 2^60 1 <= b_i <= 10^9

输出

一个整数,代表最大的小组能力值之和。如果没有两个人能够组成一个小组,输出0.

Solution

提供两种思路

1.拓扑排序
当a不认为自己强于b时,由b连向a一条有向边
建图后拓扑排序,每次删除入度为零的点
得到答案
2.位运算乱搞…
“能赢过我的只有我自己!”
由题意可得:相同的数和它的子集都可加入,如此这般…记录就好

…挺秃然的…

拓扑代码在下面

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define il inline
#define re register
/*using namespace std;
typedef long long ll;
const ll maxn = 7100;
ll p[maxn][70];
//每个会的算法 
ll indegree[maxn];//入度 
vector<ll> e[maxn];
ll n, m, a[maxn], b[maxn];
ll ans = 0;
il void Topu() {
	queue<ll> q;
	for(re ll i = 1; i <= n; ++i) {
		if(indegree[i] == 0) {
			q.push(i); 
		}
	}
	while(!q.empty()) {
		ll now = q.front();
		ans -= b[now];
		q.pop();
		for(re ll i = 0; i < e[now].size(); ++i) {
			ll v = e[now][i];
			indegree[v]--;
			if(indegree[v]==0) q.push(v); 
		}
	}
}
int main() {
	memset(p, 0, sizeof(p));
	scanf("%I64d", &n);
	for(re ll i = 1; i <= n; ++i) {
		scanf("%I64d", &a[i]);
		ll cnt = 0;
		while((1 << cnt) <= a[i]) {
			if(a[i] & (1<<cnt)) 
				p[i][cnt] = 1;
			cnt++;
		}
		//m = max(m, cnt-1);//最高位 
	}
	for(re ll i = 1; i <= n; ++i) {
		scanf("%I64d", &b[i]);
		ans += b[i];
	}
	for(re ll i = 1; i <= n; ++i) {
		for(re ll j = 1; j <= n; ++j) {
			if(i == j) continue;
			bool flag = 1;
			for(re ll k = 0; k <= m; ++k) {
				if(p[i][k] && !p[j][k]) {
					//i会j不会的算法=>i比j厉害 
					flag = 0;
					break;
				}
			}
			if(flag) {
				//i 没有 j厉害 => j 连向 i 
				e[j].push_back(i);
				indegree[i]++;
			}
		}
	}
	Topu();
	printf("%I64d\n", ans);
	return 0;
}*/
//a不认为自己强于b时,b连向a 
using namespace std;
typedef long long ll;
ll a[7100];
vector<ll> v;
map<ll,ll> tp, cnt;
int main() {
    int n; 
	ll b, ans = 0;
    scanf("%d",&n);
    for(re int i = 1; i <= n; i++) {
    	scanf("%I64d", &a[i]);
    	cnt[a[i]]++;
    	//有几个相同的 
	}
	for(re int i = 1; i <= n; i++) {
    	scanf("%I64d",&b);
    	tp[a[i]] += b;
	}
	for(re int i = 1; i <= n; i++) 
	 	if (cnt[a[i]] >= 2) {
	 		//超过两个就记录下来 
	 		ll s = a[i];
        	ans = ans + tp[s];
        	tp[s] = cnt[s] = 0;
        	v.push_back(s);
	 	} 
//cout<<ans;
	for(re int i = 1; i <= n; i++)
	  	if(cnt[a[i]] > 0) {
	  //	cout<<a[i]<<"\n";
	 		for(re int j = 0; j < v.size(); j++) {
	 	 		if((v[j]|a[i]) == v[j]) {//枚举子集 
	 	 			ll s = a[i];
	 	 			ans = ans+tp[s];
       	     		tp[s] = cnt[s] = 0;//清除 
	 		 		break;
		  		}
	  		}
	 	}	
	printf("%I64d", ans);
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 7010;
int n, cnt, top, q[maxn], d[maxn];
ll a[maxn], b[maxn], ans;
vector<int> v[maxn];
bool check(ll x, ll y) {
    return y == (x & y);
}
int main () {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", a + i);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%lld", b + i);
        ans += b[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i != j && check(a[j], a[i])) {
                v[j].push_back(i);
                d[i]++;
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        if (!d[i]) {
            q[++top] = i;
            cnt++;
            ans -= b[i];
        }
    }
    while(top) {
        int x = q[top--];
        for (int i = 0; i < v[x].size(); i++) {
            int y = v[x][i];
            d[y]--;
            if (d[y] == 0) {
                q[++top] = y;
                cnt++;
                ans -= b[y];
            }
        }
    }
    if (cnt == n - 1) puts("0");
    else printf("%lld\n", ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章