CCSU团队训练赛
H - Billionaires URAL - 1650
You probably are aware that Moscow holds the first place in the world with respect to the number of billionaires living there. However, the work of billionaires is such that they have to travel a lot. That is why some other city can be the first in such a list on certain days. Your friends from FSB, FBI, MI5, and Shin Bet have provided you with information about movements of billionaires during some period of time. Your employer asks you to determine for each city the number of days during this period on which this city exceeded all other cities in the total amount of money that billionaires staying in this city have.
Input
In the first line you are given the number n of billionaires (1 ≤ n ≤ 10000) . The following n lines contain information about these people: their names, cities where they were staying at the beginning of the period, and their fortunes. In the next line you are given the number m of days in the period for which you have the information (1 ≤ m ≤ 50000) and the number k of travels of the billionaires (0 ≤ k ≤ 50000) . The following k lines contain the list of travels in the following format: the number of the day (from 1 to m−1), the name of the person, and the city of destination. You may assume that billionaires depart late at night and arrive to the destination city on the next day's morning. They cannot make more than one travel each day. The numbers of days in the list are not decreasing. All names of people and cities consist of at most 20 English letters; you must take into consideration the case of the symbols. The fortunes are in the range from 1 to 100 billions (one billion is a thousand million).
Output
In each line of the output give the name of a city and, after a space, the number of days during which this city was the first with respect to the sum of fortunes of the billionaires staying there. Leave out those cities for which there were no such days. The cities must be sorted alphabetically (with the usual symbol order: ABC...Zabc...z).
Example
input | output |
---|---|
5 Abramovich London 15000000000 Deripaska Moscow 10000000000 Potanin Moscow 5000000000 Berezovsky London 2500000000 Khodorkovsky Chita 1000000000 25 9 1 Abramovich Anadyr 5 Potanin Courchevel 10 Abramovich Moscow 11 Abramovich London 11 Deripaska StPetersburg 15 Potanin Norilsk 20 Berezovsky Tbilisi 21 Potanin StPetersburg 22 Berezovsky London | Anadyr 5 London 14 Moscow 1 |
题解:恶心的题线段树模拟记住是先算贡献再更新
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
struct segement{
ll maxn, maxn1;
int pos;
}tree[4 * N];
ll a[N];
#define lson 2 * node
#define rson 2 * node + 1
#define m (l + r) / 2
struct data{
string na, ad;
};
void build(int l, int r, int node){
if(l == r){
tree[node].maxn = a[l];
tree[node].maxn1 = 0;
tree[node].pos = l;
return;
}
build(l, m, lson);
build(m + 1, r, rson);
if(tree[rson].maxn > tree[lson].maxn){
tree[node].maxn = tree[rson].maxn;
tree[node].pos = tree[rson].pos;
if(tree[rson].maxn1 > tree[lson].maxn){
tree[node].maxn1 = tree[rson].maxn1;
}else{
tree[node].maxn1 = tree[lson].maxn;
}
}else{
tree[node].maxn = tree[lson].maxn;
tree[node].pos = tree[lson].pos;
if(tree[lson].maxn1 > tree[rson].maxn){
tree[node].maxn1 = tree[lson].maxn1;
}else{
tree[node].maxn1 = tree[rson].maxn;
}
}
}
void update(ll v, int pos, int l, int r, int node){
if(l == r){
tree[node].maxn += v;
return;
}
if(pos <= m) update(v, pos, l, m, lson);
else update(v, pos, m + 1, r, rson);
if(tree[rson].maxn > tree[lson].maxn){
tree[node].pos = tree[rson].pos;
tree[node].maxn = tree[rson].maxn;
if(tree[rson].maxn1 > tree[lson].maxn){
tree[node].maxn1 = tree[rson].maxn1;
}else{
tree[node].maxn1 = tree[lson].maxn;
}
}else{
tree[node].maxn = tree[lson].maxn;
tree[node].pos = tree[lson].pos;
if(tree[lson].maxn1 > tree[rson].maxn){
tree[node].maxn1 = tree[lson].maxn1;
}else{
tree[node].maxn1 = tree[rson].maxn;
}
}
}
vector<data> g[N];
map<string , string> name;
map<string, int> address;
map<string, ll>money;
map<string, int>ans;
string cnt[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, day, q;
cin >> n;
int top = 1;
for(int i = 1; i <= n; i++){
string na, ad;
ll w;
cin >> na >> ad >> w;
money[na] = w;
name[na] = ad;
if(address[ad]){
a[address[ad]] += w;
}else{
a[top] = w;
cnt[top] = ad;
address[ad] = top++;
}
}
int maxn = 100000;
build(1, maxn, 1);
cin >> day >>q;
while(q--){
int d;
string ad, na;
cin >> d>> na>> ad;
g[d].push_back({na, ad});
}
for(int i = 1; i <= day; i++){
if(tree[1].maxn1 != tree[1].maxn){
ans[cnt[tree[1].pos]]++;
}
for(int j = 0; j < g[i].size(); j++){
string na = g[i][j].na;
string ad = g[i][j].ad;
string last_ad = name[na];
name[na] = ad;
int pos = address[last_ad];
ll va = money[na];
update(-va, pos, 1, maxn, 1);
if(address[ad] == 0){
cnt[top] = ad;
address[ad] = top++;
}
pos = address[ad];
va = money[na];
update(va, pos, 1, maxn, 1);
}
}
for(auto it: ans){
cout <<it.first <<" " << it.second << endl;
}
}
/*
5
Abramovich London 10000000000
Deripaska Moscow 10000000000
Potanin Moscow 5000000000
Berezovsky London 2500000000
Khodorkovsky Chita 1000000000
25 9
1 Abramovich Anadyr
5 Potanin Courchevel
10 Abramovich Moscow
11 Abramovich London
11 Deripaska StPetersburg
15 Potanin Norilsk
20 Berezovsky Tbilisi
21 Potanin StPetersburg
22 Berezovsky London
*/
G - Sum of Digits URAL - 1658
题意: 给你s1, s2分别表示 一个数每个数字之和等于s1 每个数字之间数字的平方和等于s2
求一个最小的这样的数字。如果没用输出 No solution 如果这个数字的长度大于100输出
No solution
题解:
设 \(dp[i][j]\) 表示每个数字之和为i, 每个数字平方和为j能得到的最小长度。
初始化时 把\(dp\)数组赋值为无穷大,\(dp[0][0] = 0\)
那么\(dp[i][j]\) = \(min(dp[i][j], dp[i - k][i - k *k ])\) 其中k为 1到9
知道每种状态最优的最小长度,怎么才能求出答案呢?
我们可以贪心。
\(if(dp[s1 - k][s2 - k * k] + 1 == dp[s1][s2])\) 其中k 是从1 到 9 开始贪心
如果这个条件成立, 那么 第1位数就可以选 k, 然后 s1 = s1 - k, s2 = s2 - k *k
一直贪最小的最后得到一个最优解
#include<bits/stdc++.h>
using namespace std;
int dp[1000][10000];
int s1, s2;
int main(){
for(int i = 0; i < 1000; i++){
for(int j = 0; j < 10000; j++){
dp[i][j] = 1000;
}
}
dp[0][0] = 0;
for(int i = 1; i <= 9; i++){
for(int j = 1; j < 1000; j++){
for(int k = 1; k < 10000; k++){
if(j - i >= 0 && k - i * i >= 0)
dp[j][k] = min(dp[j][k], dp[j - i][k - i * i] + 1);
}
}
}
int t; scanf("%d", &t);
while(t--){
scanf("%d %d", &s1, &s2);
if(s1 >= 1000 || s2 >= 10000){
printf("No solution\n");
}else if(dp[s1][s2] > 100){
printf("No solution\n");
}else{
vector<int>v;
int ans = dp[s1][s2];
while(1){
for(int i = 1; i <= 9; i++){
if(dp[s1 - i][s2 - i * i] + 1 == dp[s1][s2]){
v.push_back(i);
s1 = s1 - i;
s2 = s2 - i * i;
break;
}
}
if(v.size() == ans){
break;
}
}
for(int i: v){
printf("%d", i);
}
puts("");
}
}
}
Shortest Subchain URAL - 1651
题意:给你一个串数字, 表示从第一位置 到最后一个位置走的路径, 问找一个从第一个位置到最后一个位置的最短路径, 且走的边的顺序和给的顺序一样(也就是 后走的边一定不能再先走的边前面)。
题解:
这题看起来好难写, 但是会了建图这题就简单了。
要是没用后面的条件很多直接建图跑个BFS就可以了。但是有了这个条件就无从下手。
我们可以, 按照位置建图, 也就是第一个点的位置与第二个点的位置(i --> i + 1)建立一条有向边且边权为1, 这样可以保证走的路径一定是按照题目的意思。
然后我再找 a[i]再i位置前面离i最近的点建一天权值为0的有向边, 图建好了, 就是简单的迪杰斯特拉了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
int a[N], n, pos[N];
struct node{
int u, v;
bool operator <(const node a)const{
return v > a.v;
}
};
vector<pair<int, int> >g[N];
priority_queue<node>q;
int dist[N], vis[N], fa[N];
void dij(){
q.push({1, 0});
for(int i = 1; i <= n; i++){
vis[i] = 0;
dist[i] = 1e8;
fa[i] = 0;
}
dist[1] = 0;
while(q.size()){
node cd = q.top();
q.pop();
if(vis[cd.u])continue;
for(auto it: g[cd.u]){
int to = it.first;
int cost = it.second;
if(dist[to] > dist[cd.u] + cost){
dist[to] = dist[cd.u] + cost;
q.push({to, dist[to]});
fa[to] = cd.u;
}
}
}
}
vector<int>v;
int main(){
while(~scanf("%d", &n)){
v.clear();
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
g[i].clear();
pos[a[i]] = 0;
}
for(int i = 1; i < n; i++){
g[i].push_back({i + 1, 1});
}
for(int i = 1; i <= n; i++){
if(pos[a[i]]){
g[pos[a[i]]].push_back({i, 0});
}
pos[a[i]] = i;
}
dij();
int x = n;
while(x){
v.push_back(a[x]);
x = fa[x];
}
v.push_back(-1);
reverse(v.begin(), v.end());
for(int i = 1; i < v.size(); i++){
if(v[i] == v[i - 1])continue;
printf("%d ", v[i]);
}
puts("");
}
}
M - Pairs AtCoder - abc155_d
题意:给你n个数, 每一对相乘可以组合一个数, 总共有\(\cfrac{n * (n -1)}{2}\) 也就是 第一个数与前面\(n - 1\)个做配对
第二个是与前面\(n - 2\)做配对, 以此类推。
问 这些组合的数,第k大是多少?
如果这题没用负数那就跟简单了,但是有负数操作起来就很麻烦。
题解:
A:这题怎么写的?
B:二分,随便乱搞。
A: 我想打人。。。 怎么二分的?
B:直接二分答案就行了。
A: 你是说直接二分答案吗? 也就是再 $ l = 10 ^{- 18}, r = 10 ^{18}$ 二分 , 如果当前的二分的值中比这个值大的数的个数大于等于$k $ 就 左边移动, 否则向右边移动。
B:对就是这样。
A:我想到了呀, 但是我不会判断比当前值大的数有多少个, 要是枚举的话就 \(n ^ {2}\) 的复杂度了。
B:嗯。。。继续二分。可以把复杂度降到 \(n * log(n)\) 找比当前值大的数有多少个?
A:wc, 牛皮。
B:你把正数丢进一个vector里面, 把负数丢进一个vector里面,然后按照从小到大排序。
之后就可以先枚举负数, 从最小的开始枚举, 然后\(l = i + 1, r = negative.size() - 1\) 其中\(i\) 就是你要枚举的那位, 二分查最后一个 \(negative[i] * negative[m] <= x\) 然后在记录下个数就好。
正数的也是一样。 最后再用二分查下正数和负数的。
A:我懂了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 7;
typedef long long ll;
int n;
ll k, a[N];
vector<ll>positive, negative;
bool judge(ll x){
ll ans = 0;
for(int i = 0; i < negative.size(); i++){
ll l = i + 1, r = negative.size() - 1;
ll res = r + 1;
while(l <= r){
ll m = (l + r) / 2;
if(negative[m] * negative[i] <= x){
r = m - 1;
res = m;
}else{
l = m + 1;
}
}
ans += negative.size() - res;
}
for(int i = 0; i < positive.size(); i++){
ll l = i + 1, r = positive.size() - 1;
ll res = -1;
while(l <= r){
ll m = (l + r) / 2;
if(positive[i] * positive[m] <= x){
l = m + 1;
res = m;
}else{
r = m - 1;
}
}
if(res != -1){
ans += res - i;
}
}
for(int i = 0; i < positive.size(); i++){
ll l = 0, r = negative.size() - 1;
ll res = -1;
while(l <= r){
ll m = (l + r) / 2;
if(positive[i] * negative[m] <= x){
l = m + 1;
res = m;
}else{
r = m - 1;
}
}
ans += res + 1;
}
if(ans >= k)return true;
return false;
}
map<ll, int>q;
int main(){
scanf("%d %lld", &n, &k);
for(int i = 1; i <= n; i++){
scanf("%lld", &a[i]);
if(a[i] >= 0)positive.push_back(a[i]);
else negative.push_back(a[i]);
}
sort(positive.begin(), positive.end());
sort(negative.begin(), negative.end());
ll l = -1000000000000000000;
ll r = 1000000000000000000;
ll ans;
while(l <= r){
ll m = (l + r) / 2;
if(judge(m)){
r = m - 1;
ans = m;
}else{
l = m + 1;
}
}
printf("%lld\n", ans);
}
/*
10 40
5 4 3 2 -1 0 0 0 0 0
*/