Codeforces Round #367 (Div. 2) 題解

題目鏈接:http://codeforces.com/contest/706


感想:唯一一次前四道題全部都有思路,而且能保證正確的一次CF。但是最後卻只A了前兩道水題,最後沒能來得及寫後兩道題。也許是太晚了,腦袋不太清醒吧!後面纔想到後兩題的做法。回想一下,從18分鐘寫完B後,就開始打醬油到比賽末,最後只A兩題,就憤憤不平。555555!


A.思路:水題。直接將各個點與源點比較算時間,求出最小值即可。詳見代碼。

#include <bits/stdc++.h>
using namespace std;
double a, b;
int n;

int main(){
	//ios::sync_with_stdio(false);
	//cin.tie(0);
	cin >> a >> b >> n;
	double ans = INT_MAX;
	double x, y, v;
	while (n--){
		cin >> x >> y >> v;
		ans = min(ans, sqrt((x-a)*(x-a)+(y-b)*(y-b))/v);
	}
	printf("%.8f\n", ans);
	return 0;
}

B.思路:排序後二分查找或者樹狀數組可以解決。這裏用的是樹狀數組,樹狀數組下標是輸入的數,存儲的是某區間小於等於給定數的個數。詳見代碼。注意:樹狀數組的寫法,要注意詢問時,給出的數可能大於最大下標。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int sum[maxn];
int n, q;

int lowbit(int x){
	return x&(-x);
}

void add(int p){
	while (p < maxn){
		++sum[p];
		p += lowbit(p);
	}
}

int query(int p){
	int ans = 0;
	while (p > 0){
		ans += sum[p];
		p -= lowbit(p);
	}
	return ans;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	int num;
	while (n--){
		cin >> num;
		add(num);
	}
	cin >> q;
	while (q--){
		cin >> num;
		if (num >= maxn)
			num = maxn-1;
		cout << query(num) << endl;
	}
	return 0;
}

C.思路:一開始準備枚舉一遍後,得出結果。後來發現有問題,得到的結果明顯不對。後來再仔細分析一下,發現是一道簡單DP題,狀態轉移比較簡單,不多解釋。詳見代碼。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
ll dp[maxn][2];
int cost[maxn];
string s[maxn], t[maxn];
int n;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	for (int i=1; i<=n; ++i)
		cin >> cost[i];
	for (int i=1; i<=n; ++i){
		cin >> s[i];
		t[i] = s[i];
		reverse(t[i].begin(), t[i].end());
		dp[i][0] = dp[i][1] = inf;
	}
	dp[1][0] = 0;
	dp[1][1] = cost[1];
	for (int i=2; i<=n; ++i){
		if (s[i] >= s[i-1])
			dp[i][0] = dp[i-1][0];
		if (s[i] >= t[i-1])
			dp[i][0] = min(dp[i][0], dp[i-1][1]);
		if (t[i] >= s[i-1])
			dp[i][1] = dp[i-1][0]+cost[i];
		if (t[i] >= t[i-1])
			dp[i][1] = min(dp[i][1], dp[i-1][1]+cost[i]);
	}
	ll ans = min(dp[n][0], dp[n][1]);
	cout << (ans==inf ? -1 : ans) << endl;
	return 0;
}

D.思路:一道字典樹的題。將要插入的數的二進制位倒着建樹(爲什麼?因爲異或時高位儘量大,結果才儘量大),即高位在深度低的節點上。用一個數組記錄經過各個節點的數的個數,插入時,每經過一個點,將節點的這個值加一,刪除時,則減一。查找時,當前節點的這個值大於0,說明有數經過。對於要查找的這個數的高位,如果是1,要使異或值儘量大,那麼就要往0的地方走,反之,往1的地方走,實在沒辦法走,只有按原路徑走啦。詳見代碼。注意:0永遠在樹中。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3000005;
int child[maxn][2], val[maxn];
int q, sz=1;
string op;

void _insert(int x){
	int pos = 0;
	for (int i=29; i>=0; --i){
		int id = (x>>i)&1;
		if (!child[pos][id])
			child[pos][id] = sz++;
		pos = child[pos][id];
		++val[pos];
	}
}

void _delete(int x){
	int pos = 0;
	for (int i=29; i>=0; --i){
		int id = (x>>i)&1;
		pos = child[pos][id];
		--val[pos];
	}
}

int query(int x){
	int ans=0, pos=0;
	for (int i=29; i>=0; --i){
		int id = (x>>i)&1;
		if (id == 1){
			if (child[pos][0] && val[child[pos][0]]){
				ans += 1<<i;
				pos = child[pos][0];
			}
			else
				pos = child[pos][1];
		}
		else{
			if (child[pos][1] && val[child[pos][1]]){
				ans += 1<<i;
				pos = child[pos][1];
			}
			else
				pos = child[pos][0];
		}
	}
	return ans;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> q;
	int num;
	_insert(0);
	while (q--){
		cin >> op >> num;
		if (op[0] == '+')
			_insert(num);
		else if (op[0] == '-')
			_delete(num);
		else
			cout << query(num) << endl;
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章