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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章