A
https://codeforces.com/contest/1009/problem/A
思路:
用两个指针,一个指向费用,一个指向钱,指向费用的每次右移,指向钱的在满足题意的情况下才右移。
最后输出第二个指针的位置。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
int n, m, k;
vi wal, cost;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cin >> n >> m;
cost.resize(n);
for(int i = 0; i < (int)cost.size(); i++)
cin >> cost[i];
wal.resize(m);
for(int i = 0; i < (int)wal.size(); i++)
cin >> wal[i];
int res = 0;
for(int u = 0, v = 0; u < (int)cost.size() && v < (int)wal.size(); u++){
if(cost[u] <= wal[v]){
v++;
res++;
}
}
cout << res << endl;
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
B
https://codeforces.com/contest/1009/problem/B
思路:
‘1’是万能的,也就是说‘1’是一定可以到达任何位置的。
‘0’和‘2’不是万能的,他们不能相互”穿过“。
用两种方法来获得最小。
第一,把‘2’出现之前的直接sort。
第二,‘2’出现之后, 把每个数进行计数,根据上面的‘定理’,我们知道,‘0’现在是不可能在‘2’的位置了,那只能是‘1’了,所以把一个‘1’放到‘2’的位置(如果有‘1’可以的话),然后再把这个‘2’用‘1’交换,重复这个动作。
根据上面所说的,‘1’可以到达任何位置(或者说任何位置都可以是‘1’),最终会发现第一次出现的‘2’之后的所以‘1’都到了最前面(这里说的最前面是指第一次出现‘2’的位置)。
然后‘2’与‘2’之间的‘0’是死的,动不了的,所以‘2’之间有多少个‘0’就填多少个‘0’。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
int n, m, k;
vi cnt, dif;
string str, ptr;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cin >> str;
cnt.resize(3);
bool found = false;
int u = 0;
dif.push_back(0);
for(int i = 0; i < (int)str.length(); i++){
if(str[i] == '2'){
if(!found){
for(int j = 0; j < cnt[0]; j++)
str[j] = '0';
for(int j = cnt[0]; j < i; j++)
str[j] = '1';
u = i;
cnt.clear();
cnt.resize(3);
found = true;
}
dif.push_back(0);
}
if(str[i] == '0')
dif[dif.size() - 1]++;
cnt[str[i] - '0']++;
}
if(found){
for(int i = u; i < u + cnt[1]; i++)
str[i] = '1';
for(int i = u + cnt[1], j = 1; i < (int)str.length(); j++){
str[i++] = '2';
for(int cnt = 0; cnt < dif[j]; cnt++, i++)
str[i] = '0';
}
}
else{
for(int i = u; i < u + cnt[0]; i++)
str[i] = '0';
for(int i = u + cnt[0]; i < (int)str.length(); i++)
str[i] = '1';
}
cout << str << endl;
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
C
https://codeforces.com/contest/1009/problem/C
思路:
arithmetic mean指的是平均数。
一种很直观想到的是直接对目前的平均数操作,但这样会因为精度误差导致结果错误,即使运算过程有误差优化。
所以采用记录总和最后再除的方法来最大化减少误差。
当d大于0时,要想最大化d的效果就必须选择两端作为i。
相反,要想最小化d的效果就选择中间作为i。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
ll n, m, k;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(7) << fixed;
cin >> n >> m;
ll sum = 0;
while(m--){
ll x, d;
cin >> x >> d;
sum += x * n;
if(d >= 0)
sum += d * ((n * (n - 1)) >> 1);
else
sum += d * ((((n >> 1) * (1 + (n >> 1))) >> 1) + ((((n - 1) >> 1) * (1 + ((n - 1) >> 1))) >> 1));
}
cout << (double)sum / n << endl;
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
D
https://codeforces.com/contest/1009/problem/D
思路:
首先看看是否可以暴力,发现m只有1e5,即使n再大也没法骗到机智的我们。
所以可以直接暴力找i,j。
(有关更好的解法我以后可能会加上去,就先挖个坑了。。。)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
int n, m, k;
queue< pair<int, int> > q;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cin >> n >> m;
if(m < n - 1)
cout << "Impossible" << endl;
else{
for(int i = 1; i <= n && m; i++)
for(int j = i + 1; j <= n && m; j++){
if(__gcd(i, j) - 1)
continue;
q.push(make_pair(i, j));
m--;
}
if(m)
cout << "Impossible" << endl;
else{
cout << "Possible" << endl;
while(!q.empty()){
cout << q.front().first << " " << q.front().second << endl;
q.pop();
}
}
}
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
E
https://codeforces.com/contest/1009/problem/E
思路:
首先考虑dp,dp[i]记录i位的结果。
接下来考虑转移方程,第i位的可能取值有i个,一个个来计算。
当第i位是j时,dp[i] = (dp[i] + dp[i - j] + sum[i - j] * c) % MOD;
在解释c是什么之前,先解释c前面的。
要想i位是j,那么在i - j位的时候必须休息,然后再一直不停地走,这就是dp[i - j] + sum[i - j] * c;
这里直接说明c = (1 << max(0, i - j - 1)),意思是有c种走到i - j的方法。
但是这样还不够,我们发现,这样的复杂度是O(n ^ 2),怎么办呢?
把转移方程换种形式,dp[i] = (∑dp[x] (0 < x < i) + sum[1] * 2 ^ (i - 2) + sum[2] * 2 ^ (i - 3)...)% MOD;
前面的∑可以直接记录,后面的可以发现,求dp[i] 的 是求dp[i - 1] 的两倍 再加上 p[i],这里p[i] = sum[i] - sum[i - 1];
所以就可以O(n)计算出结果了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 998244353;
const double eps = 1e-8;
int n, m, k;
vll p;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cin >> n;
p.resize(n);
for(int i = 0; i < n; i++)
cin >> p[i];
ll res, last, add;
res = last = add = 0;
for(int i = 0; i < n; i++){
add = ((add * 2) % MOD + p[i]) % MOD;
res = (last + add) % MOD;
last = (res + last) % MOD;
}
cout << res << endl;
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
未来可期。