A. Ilya and Diplomas
题意: 找到三个数 使得满足在三个范围之间 并且和为n 多种可能使得 第一个数尽可能大 第二个数尽可能大
思路: 按题意枚举一下
参考code:
//
// Created by TaoSama on 2015-06-30
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, a[3][2];
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(cin >> n) {
for(int i = 0; i < 3; ++i) for(int j = 0; j < 2; ++j) cin >> a[i][j];
int sum = a[1][0] + a[2][0];
bool ok = false;
int ans[3];
for(int i = min(n - sum, a[0][1]); i >= a[0][0]; --i) {
for(int j = min(n - i - a[2][0], a[1][1]); j >= a[1][0]; --j) {
if(n - i - j >= a[2][0] && n - i - j <= a[2][1]) {
ok = true;
ans[0] = i; ans[1] = j; ans[2] = n - i - j;
break;
}
}
if(ok) break;
}
for(int i = 0; i < 3; ++i) cout << ans[i] << ' ';
cout << '\n';
}
return 0;
}
B. Pasha and Tea
题意: 一个茶壶可以烧不超过w的水 给女生分配杯子 然后给男女生倒水 必须都有水 且每个男生水相等 每个女生相等 且每个男生水是女生的2倍
思路: 排序一下 从大到小 不断给男女生分配水 二分check一下就可以了
参考code:
//
// Created by TaoSama on 2015-06-30
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, w, a[N];
bool check(double x) {
double e = x / 3.0 / n;
for(int i = n << 1; i; --i) {
if(i > n) {
if(a[i] < 2 * e) return false;
} else {
if(a[i] < e) return false;
}
}
return true;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(cin >> n >> w) {
for(int i = 1; i <= n << 1; ++i) cin >> a[i];
sort(a + 1, a + 1 + (n << 1));
double l = 0, r = w;
for(int i = 1; i <= 100; ++i) {
double mid = (l + r) / 2.0;
if(check(mid)) l = mid;
else r = mid;
}
cout << fixed << setprecision(12) << l << endl;
}
return 0;
}
C. Arthur and Table
题意: 一个椅子n条腿 删掉每条腿有个各自的体力 平衡的条件是 最长的腿的个数必须占一半以上 求使椅子平衡的最小体力花费
思路: 枚举每条腿作为最长腿 假设它的个数是x 然后删掉所有比它长的 删掉一些比它短的(留下x-1个就好) 贪心的思路
这样看上去像时候n^2的 考虑优化 我们可以直接算出来比它长的腿的花费和 用前缀和就好了
然后发现d只有200 我们对腿长排序 在枚举腿长的时候 然后不断更新d的cnt数组 这样cnt数组里都是比这条腿小的 删除的时候从最小的开始贪心的删除
这样复杂度就优化到了 O(d * n)了
参考code:
//
// Created by TaoSama on 2015-06-30
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, d[205], l[N], sum[N];
pair<int, int> a[N];
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(cin >> n) {
memset(d, 0, sizeof d);
memset(l, 0, sizeof l);
for(int i = 1; i <= n << 1; ++i) {
if(i <= n) {
cin >> a[i].first;
l[a[i].first]++;
} else {
cin >> a[i - n].second;
}
}
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; ++i)
sum[i] = sum[i - 1] + a[i].second;
// for(int i = 1; i <= n; ++i){
// pr(a[i].first); pr(a[i].second); prln(sum[n] - sum[i]);
// }
int ans = INF;
for(int i = 1; i <= n; ++i) {
int t = i + l[a[i].first];
int cur = sum[n] - sum[t - 1];
for(int j = 1, k = i - l[a[i].first]; j <= 200 && k > 0; ++j) {
if(k >= d[j]) cur += j * d[j], k -= d[j];
else cur += j * k, k = 0;
}
// prln(cur);
// for(int j = 1; j <= 200; ++j){
// if(d[j]) pr(j), prln(d[j]);
// }
// cout << endl;
ans = min(ans, cur);
for(int j = i; j < t; ++j) d[a[j].second]++;
i = t - 1;
}
cout << ans << '\n';
}
return 0;
}
/*
2
1 5
3 2
3
2 4 4
1 1 1
6
2 2 1 1 3 3
4 3 5 5 2 1
*/
D. Vitaly and Cycle
思路: 一个奇回路的边数最少是3 那么这个题的边数答案应该是 [0,3]
考虑 3的方法数 这个时候图应该是没有边 全是散点 那么方法数应该是 C_n_3 (n个点里选3个点连3条边) m = 0
考虑 2的方法数 这个时候图应该是 必然有2个点构成的长度为1的链(边) 以及 可能有多余的散点 这时候每个点度数都<=1
将这条边向任意其它1点连2条边 构成奇回路 方法数是C_n-2_1(n-2点里选1点连2条边)
考虑 1的方法数 这个时候图应该是 有大于2点构成的连通分量 这个时候用二分图染色 如果可以染色说明 只有偶回路
不然有奇回路 就是添加0条边了 方法数 对于每个连通分量任意两个相同颜色连1条边
所以都是 C_cnt_2 ans1 += cnt1 * (cnt1 - 1) / 2 + cnt2 * (cnt2 - 1) / 2.
考虑 0的方法数 显然只有不加这一种方法 所以是1
参考code:
//
// Created by TaoSama on 2015-06-30
// Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, m, c[N];
long long ans[5], cnt[2];
vector<int> G[N];
bool dfs(int u, int cc) {
c[u] = cc; cnt[cc]++;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(c[v] == cc) return false;
if(c[v] == -1 && !dfs(v, cc ^ 1)) return false;
}
return true;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(cin >> n >> m) {
for(int i = 1; i <= n; ++i) G[i].clear();
for(int i = 1; i <= m; ++i) {
int u, v; cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
memset(c, -1, sizeof c);
memset(ans, 0, sizeof ans);
if(m == 0) ans[3] = 1LL * n * (n - 1) * (n - 2) / 6;
bool two = true;
for(int i = 1; i <= n; ++i) two &= G[i].size() <= 1;
if(two) ans[2] = 1LL * m * (n - 2);
bool zero = false;
for(int i = 1; i <= n; ++i) {
memset(cnt, 0, sizeof cnt);
if(c[i] == - 1 && !dfs(i, 0)) {
zero = true;
break;
} else ans[1] += cnt[0] * (cnt[0] - 1) / 2 + cnt[1] * (cnt[1] - 1) / 2;
}
if(zero) cout << "0 1\n";
else if(ans[1]) cout << 1 << ' ' << ans[1] << '\n';
else if(ans[2]) cout << 2 << ' ' << ans[2] << '\n';
else cout << 3 << ' ' << ans[3] << '\n';
}
return 0;
}
E. Ann and Half-Palindrome
题意: 求|s|<=5000 的只有奇数位置是回文的 字典序的第k个字串
思路: 填坑