2019 ICPC Northwestern European(6/11)
作者:互联网
2019-2020 ICPC Northwestern European Regional Programming Contest (NWERC 2019)
目录A. Average Rank
大意:
n 个参赛选手将进行 w 轮的比赛,每轮比赛都会有一些选手加一分,选手按照分数从大到小排名(分数相等并列)。计算每个选手在这w轮比赛中排名的平均值。
思路:
当一个人分数增加时,所有和他分数相同的人rank+1,同时他的rank-(他当前分数+1的这个分数有多少人)
分别维护每个分数对最后总排名的贡献,类似线段树中lazy数组的思想,等用到这个分数的时候更新当前总贡献
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, w;
LL rk[N], sum[N], last[N], f[N], pre[N], num[N];
int main() {
cin >> n >> w;
for (int i = 0; i < w; i++) {
int m;
cin >> m;
for (int j = 0; j < m; j++) {
int x;
cin >> x;
//当前分被更新
num[f[x]] +=rk[f[x]] *(i - last[f[x]]);
// 这个分数当前贡献 += 这个分数保持了多少轮 *这个分数当前名次
last[f[x]] = i; //记录上次更新的位置
rk[f[x]]++; //这个分的排名下降了 跟他名次相同的人的名次下降了
sum[x] += num[f[x]] - pre[x];
// 这个人添加的贡献为 当前分的总贡献-上次分的贡献
//+1的分数也被更新
f[x]++; // 这个人分数+1
num[f[x]] += rk[f[x]] * (i - last[f[x]]);
//这个分当前贡献 += 这个分数保持了多少轮 * 这个分数当前名次
last[f[x]] = i; //记录上次更新的位置
pre[x] = num[f[x]]; //记录下这个人当前的贡献变为涨完分之后的贡献
}
}
for(int i=1;i<=n;i++) //把还没更新人的贡献加上
{
num[f[i]]+=rk[f[i]]*(w-last[f[i]]);
last[f[i]]=w;
sum[i]+=num[f[i]]-pre[i];
printf("%.9f\n",1.0+1.0*sum[i]/w);
}
return 0;
}
C. Canvas Line
大意:
一条直线上面挂着很多毛巾,每个毛巾不重叠,但是可能相邻,要求每个毛巾有且只有两个夹子将它夹住,现在已经给出了一些夹子,问还需要最少多少个夹子才能满足条件,或者判断不可能满足条件
思路:
从左到右贪心即可,如果能放到这个毛巾的右边界则放
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int const MAXN = 2e5 + 10;
int n, m, T;
int l[MAXN], r[MAXN];
vector<int> pos;
vector<int> ans;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n;
for (int i = 1; i <= n; i++) cin >> l[i] >> r[i];
cin >> m;
for (int i = 0; i < m; i++) {
int x;
cin >> x;
pos.push_back(x);
}
for (int i = 1; i <= n; i++) {
int num = 0, f1 = 0, f2 = 0;
for (int j = 0; j < pos.size(); j++) {
if (pos[j] >= l[i] && pos[j] <= r[i]) num++;
if (pos[j] == r[i] - 1) f1 = 1;
if (pos[j] == r[i]) f2 = 1;
}
if (num > 2) {
cout << "impossible";
return 0;
}
if (num == 2) continue;
if (r[i] == l[i + 1]) {
int next_num = 0, next_f1 = 0;
for (int j = 0; j < pos.size(); j++) {
if (pos[j] >= l[i + 1] && pos[j] <= r[i + 1]) next_num++;
if (pos[j] == l[i + 1]) next_f1 = 1;
}
if (next_num > 2) {
cout << "impossible";
return 0;
}
if (num == 0) {
if (next_num == 2 || next_f1 == 1) {
pos.push_back(r[i] - 2);
ans.push_back(r[i] - 2);
pos.push_back(r[i] - 1);
ans.push_back(r[i] - 1);
} else {
pos.push_back(r[i] - 1);
ans.push_back(r[i] - 1);
pos.push_back(r[i]);
ans.push_back(r[i]);
}
} else {
if (next_num == 2 || next_f1 == 1) {
if (f1) {
pos.push_back(r[i] - 2);
ans.push_back(r[i] - 2);
} else {
pos.push_back(r[i] - 1);
ans.push_back(r[i] - 1);
}
} else {
if (f2) {
pos.push_back(r[i] - 1);
ans.push_back(r[i] - 1);
} else {
pos.push_back(r[i]);
ans.push_back(r[i]);
}
}
}
} else {
if (num == 0) {
pos.push_back(r[i] - 1);
ans.push_back(r[i] - 1);
pos.push_back(r[i]);
ans.push_back(r[i]);
} else {
if (f2 == 1) {
pos.push_back(r[i] - 1);
ans.push_back(r[i] - 1);
} else {
pos.push_back(r[i]);
ans.push_back(r[i]);
}
}
}
}
sort(ans.begin(), ans.end());
cout << ans.size() << endl;
for (int i = 0; i < ans.size(); i++) {
cout << ans[i] << " ";
}
cout << endl;
return 0;
}
E. Expeditious Cubing
这题主要是告诉我们必须要写稳态函数。。。
#include <bits/stdc++.h>
using namespace std;
int const MAXN = 2e5 + 10;
int n, m, T;
long double eps = 1e-8;
int sgn(long double x) {
if (fabs(x) < eps) return 0;
if (x < 0)
return -1;
else
return 1;
}
signed main() {
long double a[10];
cin >> a[0] >> a[1] >> a[2] >> a[3];
long double target;
cin >> target;
sort(a, a + 4);
long double l = (a[0] + a[1] + a[2]);
long double r = (a[1] + a[2] + a[3]);
// printf("%.2lf %.2lf\n", l / 3, r / 3);
if (sgn(l - target * 3) <= 0 && sgn(target * 3 - r) < 0) {
printf("%.2Lf\n", target * 3 - a[1] - a[2]);
} else if (sgn(target * 3 - l) < 0)
cout << "impossible\n";
else if (sgn(target * 3 - r) >= 0)
cout << "infinite\n";
return 0;
}
F. Firetrucks Are Red
大意:
现在有n个人,每个人都有一个数字的集合,如果两个人之间的数字集合有交集,那么就可以连一条边,问最后能否将所有的人联通
思路:
并查集做即可,最后输出路径跑一遍bfs即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, f[N], c[N], pre[N], vis[N];
vector<int> d[N];
vector<int> item;
vector<pair<int, int>> mp[N];
unordered_set<int> st;
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int a, int b, int c) {
int fa = findf(a), fb = findf(b);
if (fa != fb) {
f[fa] = fb;
}
mp[a].push_back({b, c});
mp[b].push_back({a, c});
}
queue<int> q;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = 1; i <= n; i++) {
int m;
cin >> m;
for (int j = 1; j <= m; j++) {
int x;
cin >> x;
d[i].push_back(x);
item.push_back(x);
}
}
sort(item.begin(), item.end());
item.erase(unique(item.begin(), item.end()), item.end());
int root,sw;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < d[i].size(); j++) {
int x =
lower_bound(item.begin(), item.end(), d[i][j]) - item.begin();
if (c[x] == 0) {
c[x] = i;
root = i;
sw = d[i][j];
} else {
Union(i, c[x], d[i][j]);
}
}
}
for (int i = 1; i <= n; i++) {
int fx = findf(i);
st.insert(fx);
}
if (st.size() != 1)
cout << "impossible" << endl;
else {
q.push(root);
q.push(sw);
vis[root] = 1;
while (!q.empty()) {
int now = q.front();
q.pop();
int w = q.front();
q.pop();
//if (vis[now]) continue;
//vis[now] = 1;
for (int i = 0; i < mp[now].size(); i++) {
int ne = mp[now][i].first,w=mp[now][i].second;
if (vis[ne]) continue;
vis[ne] = 1;
q.push(ne);
q.push(w);
cout << now << ' ' << ne << ' ' << w << endl;
}
}
}
return 0;
}
G. Gnoll Hypothesis
大意:
有 n 只怪物,每只怪物的生成概率为 \(p_i\) 。只有 k 只怪物会生成(任意 k只),不生成的怪物原本的生成概率将会向后转移给下一只会可能生成的怪物,如果是第n个那么就转移给第一个,问最终每个怪物生成的概率是多少
思路:
每个怪物最多由前面n-k个怪物转移过来,那么很容易得到转移式子:
\(p_i=∑^{n−k}_{j=0}p_{i−j}⋅C^{k−1}_{n−j−1}/C^k_n\)
#include <bits/stdc++.h>
using namespace std;
const int N = 5e2 + 5;
typedef long long LL;
int n, k;
long double a[N], res[N];
long double c[N][N]; // 记录答案
// 预处理
void init() {
for (int i = 0; i < N; ++i)
for (int j = 0; j <= i; ++j)
if (j == 0)
c[i][j] = 1;
else
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]);
}
int main() {
cin >> n >> k;
init();
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) {
for (int j = i; j >= i - (n - k); j--) {
res[i] += 1.0L * c[n - (i - j) - 1][k - 1] / c[n][k] * a[(j+n) % n];
}
printf("%.12Lf ", res[i]);
}
return 0;
}
I. Inverted Deck
大意: 给你一个长度为n的数列,问你这个数列是否为特殊数列。特殊数列为一段单调递增的数列,选择一段区间[l, r]进行翻转后形成的数列。
思路: 思维。因为特殊数列只有一段反转,那么记反转点为k1, k2。反转后,a[k1+1]<a[k1], a[k2-1]>a[k2]。因此当我第一次发现a[i]>a[i+1],那就是发现特殊数列的头部,然后我只需要从这个位置开始往后遍历,找到第一个非递减的位置即a[i-1]>a[i],cnt++。继续扫描,如果还能出现这样的情况,那就不是,break。结束后判断cnt个数是否为1即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n;
int a[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
a[0] = 0, a[n + 1] = 0x3f3f3f3f;
int flag = 0, l = 0, r = 0;
for (int i = 1; i <= n; i++) {
if (a[i] > a[i - 1]) {
if (flag == 1) {
flag = 2;
r = i - 1;
}
}
if (a[i] < a[i - 1]) {
if (flag == 2) {
cout << "impossible" << endl;
return 0;
}
if (flag == 0) {
for (int j = i - 2; j >= 0; j--) {
if (a[j] != a[j + 1]) {
l = j + 1;
break;
}
}
}
flag = 1;
}
}
if (flag == 1) r = n;
if (flag == 0) {
cout << 1 << ' ' << 1 << endl;
} else if (a[l] <= a[r + 1] && a[r] >= a[l - 1]) {
cout << l << ' ' << r << endl;
} else {
cout << "impossible" << endl;
}
return 0;
}
标签:11,Northwestern,分数,item,int,cin,long,++,European 来源: https://www.cnblogs.com/dyhaohaoxuexi/p/14530957.html