目录
题目链接:
https://codeforces.com/contest/1307
A. Cow and Haybales
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, m, k;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
for (int cs = 1; cs <= T; cs++) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int res = a[1];
int d = 0;
for (int i = 2; i <= n; i++) {
if (d + a[i] * (i - 1) <= m) {
res += a[i];
d += a[i] * (i - 1);
} else {
int x = (m - d) / (i - 1);
res += x;
break;
}
}
cout << res << endl;
}
return 0;
}
B. Cow and Friend
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;//inf=(1ll<<60)
const int N = 1e6 + 10;
ll a[N],x;
int n, m;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
for (int cs = 1; cs <= T; cs++) {
cin >> n >> x;
ll res = inf;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (x >= a[i] && (x % a[i] == 0)) {
res = min(res, x / a[i]); //全部用一种长度铺满
}
if (x <= a[i] * 2) {
res = min(res, 2ll); // 两根就能到
}
}
// 用最长的长度铺直线
sort(a + 1, a + 1 + n, greater<ll>());
if (x >= a[1]) {
ll cnt = x / a[1];
//三角形
if (x % a[1]) { // 还差一点点 再拿一根和最后铺的那根组成三角形
cnt++;
}
res = min(res, cnt);
}
cout << res << endl;
}
return 0;
}
C. Cow and Message
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n, m, k;
string s;
int cnt[N][26];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> s;
n = s.length();
s = " " + s;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 26; j++) {
cnt[i][j] = cnt[i - 1][j];
}
cnt[i][s[i] - 'a']++;
}
ll res = 0;
for (int i = 0; i < 26; i++) {
res = max(res, 1ll * cnt[n][i]);
}
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 26; j++) {
ll sum = 0;
for (int l = 1; l <= n; l++) {
if (s[l] == i + 'a') {
sum += cnt[n][j] - cnt[l][j];
}
}
res = max(res, sum);
}
}
cout << res << endl;
return 0;
}
D. Cow and Fields · 最短路最长
在图中给定点中选两个添加一条边,使得最短路最长
求出1号起始点到达各个点的距离 d1(x)
,和n号终点到达各个点的距离 d2(x)
,最终的答案求得便是
max
(
d
1
(
u
)
+
1
+
d
2
(
u
)
)
\max(\,d1(u)+1+d2(u)\,)
max(d1(u)+1+d2(u))
尝试对每一对点对进行添边操作,最短路必定存在于
min
(
d
1
(
u
)
+
1
+
d
2
(
v
)
,
d
1
(
v
)
+
1
+
d
2
(
u
)
)
\min(\,d1(u)+1+d2(v)\,,\,d1(v)+1+d2(u)\,)
min(d1(u)+1+d2(v),d1(v)+1+d2(u))之中,现在要使最短路最大,按照下面的规则,对每一对点进行排序,
d
1
(
u
)
+
1
+
d
2
(
v
)
≤
d
1
(
v
)
+
1
+
d
2
(
u
)
d1(u)+1+d2(v)\le d1(v)+1+d2(u)
d1(u)+1+d2(v)≤d1(v)+1+d2(u)
d
1
(
u
)
−
d
2
(
u
)
≤
d
1
(
v
)
−
d
2
(
v
)
d1(u)-d2(u)\le d1(v)-d2(v)
d1(u)−d2(u)≤d1(v)−d2(v)
⇒
u
<
v
\Rightarrow u<v
⇒u<v求出符合的顺序假设:
a
<
b
<
c
a<b<c
a<b<c,根据规则可以得到下面三个关系:
d
1
(
a
)
+
1
+
d
2
(
b
)
≤
d
1
(
b
)
+
1
+
d
2
(
a
)
d1(a)+1+d2(b)\le d1(b)+1+d2(a)
d1(a)+1+d2(b)≤d1(b)+1+d2(a)
d
1
(
b
)
+
1
+
d
2
(
c
)
≤
d
1
(
c
)
+
1
+
d
2
(
b
)
d1(b)+1+d2(c)\le d1(c)+1+d2(b)
d1(b)+1+d2(c)≤d1(c)+1+d2(b)
d
1
(
a
)
+
1
+
d
2
(
c
)
≤
d
1
(
c
)
+
1
+
d
2
(
a
)
d1(a)+1+d2(c)\le d1(c)+1+d2(a)
d1(a)+1+d2(c)≤d1(c)+1+d2(a)
且根据题目要求求最大,右边式子被舍去只留下左边(主人公优先走最短路),即在左边的等式中求出最大的那一个(不会有式子缺少,否则会引起排序变化)
单从上面是看不出左边哪个式子权值最大,因为前后式子之间没有直接or间接的关系,显然需要暴力找了,假设固定右端点d2,显然如果符合条件的话左端点d1的值应该希望是尽可能大的,
并且,假设存在关系: a < b < c < d < e a<b<c<d<e a<b<c<d<e 中,固定右端点 d 2 ( c ) d2(c) d2(c),能成为左端点的只有 d 1 ( a ) d1(a) d1(a)、 d 1 ( b ) d1(b) d1(b)中较大的那一个,
如果选择了后面的点,比如说构成 d 1 ( e ) + 1 + d 2 ( c ) d1(e)+1+d2(c) d1(e)+1+d2(c)(右侧的式子) ,通过排序可知,连接两点后,图上存在更短的路: d 1 ( c ) + 1 + d 2 ( e ) d1(c)+1+d2(e) d1(c)+1+d2(e)(左侧的式子),这显然并非正确答案
因此,对于每个点来说,假设当前点为边的一端,则另一端一定为在该点之前出现的那一堆点中d1最大的那个
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10;
vector<int> e[N];
int vis[N];
int n, m, k;
int d1[N], d2[N], inq[N];
struct MinDist {
int to, dist;
bool operator<(const MinDist &b) const {
return dist > b.dist;
}
};
priority_queue<MinDist> q;
void Dijkstra(int st, int d[]) {
fill(d + 1, d + 1 + n, INF);
fill(inq + 1, inq + 1 + n, 0);
d[st] = 0;
q.push({st, d[st]});
while (!q.empty()) {
int u = q.top().to;
q.pop();
if (inq[u])continue;
inq[u] = 1;
for (int v:e[u]) {
if (d[v] > d[u] + 1) {
d[v] = d[u] + 1;
q.push({v, d[v]});
}
}
}
}
vector<int> special;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m >> k;
for (int i = 1, x; i <= k; i++) {
cin >> x;
special.push_back(x);
vis[x] = 1;
}
int flag = 0, u, v;
while (m--) {
cin >> u >> v;
if (vis[u] && vis[v]) {
flag = 1;
}
e[u].push_back(v);
e[v].push_back(u);
}
if (flag) {
Dijkstra(1, d1);
cout << d1[n] << endl;
} else {
Dijkstra(1, d1);
Dijkstra(n, d2);
sort(special.begin(), special.end(), [](int u, int v) {
return d1[u] + 1 + d2[v] < d1[v] + 1 + d2[u];
});
int res = 0;
int Mx = d1[special[0]];
for (int i = 1; i < k; i++) {
res = max(res, Mx + 1 + d2[special[i]]);
Mx = max(Mx, d1[special[i]]);
}
res = min(res, d1[n]);//添边 添了个寂寞
cout << res << endl;
}
return 0;
}
E. Cow and Treats · 组合数
https://www.cnblogs.com/ttttttttrx/p/12372422.html
https://www.cnblogs.com/heyuhhh/p/12326887.html
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const int N = 1e6 + 10;
int n, m, k;
int s[N], h[N], f[N], l[N], r[N];
vector<int> v[N];
ll Mul(ll a, ll b) {
return (a * b) % mod;
}
ll Add(ll a, ll b) {
return (a + b) % mod;
}
ll qpow(ll a, ll b) {
a %= mod;
ll res = 1;
while (b) {
if (b & 1)res = Mul(res, a);
b >>= 1;
a = Mul(a, a);
}
return res;
}
ll Inv(ll a) {
return qpow(a, mod - 2);
}
ll Div(ll a, ll b) {
return Mul(a, Inv(b));
}
int x[N], y[N];
void run() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> s[i];
r[s[i]]++;
}
for (int i = 1; i <= m; i++) {
cin >> f[i] >> h[i];
v[f[i]].push_back(h[i]);
}
for (int i = 1; i <= n; i++) {
sort(v[i].begin(), v[i].end());
}
pii ans(0, 0);
auto update = [&](int x, int y) {
if (x > ans.first) ans = {x, 0};
if (x == ans.first) ans.second = Add(ans.second, y);
};
auto calc = [&](int i) {
int A = upper_bound(v[i].begin(), v[i].end(), l[i]) - v[i].begin();// 右边的牛能到达当前位置的个数
int B = upper_bound(v[i].begin(), v[i].end(), r[i]) - v[i].begin();// 左边的牛能到达当前位置的个数
int cnt1 = A * B - min(A, B); // 两只睡牛的情况 A * B - min(A, B) 组合一下减去重复的部分
int cnt2 = A + B; // 一只睡牛
if (cnt1 > 0) {
x[i] = 2;// 睡牛的个数
y[i] = cnt1;//种类数
} else if (cnt2 > 0) {
x[i] = 1;
y[i] = cnt2;
} else {
x[i] = 0;
y[i] = 1;
}
};
int tx = 0, ty = 1;// 初始值 无论怎么安排都无解
// 先遍历 只有一头睡牛的情况
for (int i = 1; i <= n; i++) {
calc(i);
tx += x[i];
ty = Mul(ty, y[i]);
}
//统计所有答案
update(tx, ty);
// 再遍历一遍每一个位置 增加可以做到两头睡牛的情况
for (int i = 1; i <= n; i++) {
int now = s[i];
tx -= x[now]; // 将原来小的答案减去
ty = Div(ty, y[now]);
l[now]++, r[now]--;
if (binary_search(v[now].begin(), v[now].end(), l[now])) { // 如果有牛从右侧出发 会睡在当前l[i]的位置
int t = upper_bound(v[now].begin(), v[now].end(), r[now]) - v[now].begin(); // 查看左侧出发的牛不超过r[i]的有几只
if (r[now] >= l[now]) --t;// 如果查到了本身 把睡在当前位置的牛减去
// 因为没有两头牛h[i]是重复的 所以 得到的t就是第i个牛睡在l[i]位置上时 与其他牛能组合出来的情况
if (t > 0) {
x[now] = 2;
y[now] = t;
} else { // 只有与1头牛 第i个牛睡在l[i]的位置上只有1种方法
x[now] = y[now] = 1;
}
int nx = tx + x[now], ny = Mul(ty, y[now]); // 叠加答案
update(nx, ny);
}
calc(now);
tx += x[now];
ty = Mul(ty, y[now]);
}
cout << ans.first << " " << ans.second << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
run();
return 0;
}