文章目录
刷题之Codeforces Round #776 (Div. 3)
1650A. Deletions of Two Adjacent Letters
-
思路:由于 ∣ s ∣ |s| ∣s∣ 为奇且每次删两个,于是可选择的字符必到某一边有偶数个。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; int cas, n, m, a[N]; string s; char c; int main(){ cin >> cas; while(cas--){ cin >> s >> c; int sz = s.size(); int ok = 0; rep(i, 0, sz - 1){ if(i % 2 == 1) continue; if(s[i] == c) ok = 1; } puts(ok ? "YES" : "NO"); } } /* 5 abcde c abcde b x y aaaaaaaaaaaaaaa a contest t */
1650B. DIV + MOD
-
题目:找到在区间 [ l , r ] [l,r] [l,r] 中的数 x x x,使得 x / a + x % a x / a+x\%a x/a+x%a 最大
-
思路:首先肯定要找 r r r 对应的 r / a + r % a r/a+r\%a r/a+r%a,其次可查看一下商为 r / a − 1 r/a-1 r/a−1,余数为 a − 1 a-1 a−1 的数是否在区间内,拿来比较一下。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e5 + 5; const db eps = 1e-10; ll cas, n, l, r, a, ans; int main(){ // freopen("1.in","r",stdin); ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin >> cas; while(cas--){ cin >> l >> r >> a; if(a == 1) cout << r << endl; else{ ll t = r / a, rr = r % a; ans = 0; if(a * (t - 1) + a - 1 >= l) ans = t - 1 + a - 1; ans = max(ans, t + rr); cout << ans << endl; } } } /* */
1650C. Weight of the System of Nested Segments
-
思路:
- 首先对所有点按照权值排序找到最小的 2 ⋅ n 2\cdot n 2⋅n 个数,标记来选择上。
- 再按照位置排序,从左右两端向中间靠近,对应选择区间。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 2e5 + 5; const db eps = 1e-10; int cas, n, m; ll sum; struct AC{ int num, val, choose, pos; }a[N]; bool cmp(AC a, AC b){ return a.val < b.val; } bool cmp2(AC a, AC b){ return a.pos < b.pos; } vector<PII> ans; int main(){ cin >> cas; while(cas--){ cin >> n >> m; rep(i, 1, m){ scanf("%d%d", &a[i].pos, &a[i].val); a[i].num = i, a[i].choose = 0; } sort(a + 1, a + 1 + m, cmp); sum = 0; rep(i, 1, 2 * n){ a[i].choose = 1; sum += a[i].val; } sort(a + 1, a + 1 + m, cmp2); ans.clear(); int l = 1, r = m, cnt = 0; rep(i, 1, n){ while(a[l].choose == 0) l++; while(a[r].choose == 0) r--; ans.push_back({a[l].num, a[r].num}); l++, r--; } cout << sum << endl; for(auto i : ans) printf("%d %d\n", i.first, i.second); } } /* 3 3 8 0 10 -2 1 4 10 11 20 7 -1 9 1 2 3 5 -2 3 6 -1 2 1 3 3 -1 2 4 4 0 8 2 2 5 5 -1 3 -2 1 0 -2 0 -5 -3 */
1650D. Twist the Permutation
-
思路:由于 n ≤ 2 e 3 n\le 2e3 n≤2e3 数据范围较小,可以用 O ( n 2 ) O(n^2) O(n2) 复杂度。
- 首先发现每次第 i i i 次操作要动前 i i i 个值时,后面 n − i n-i n−i 个数 i + 1 , i + 2 , . . . , n i+1,i+2,...,n i+1,i+2,...,n 是不变的。于是可以从后向前看,比如先查找第 n n n 次把 n n n 从第 n n n 个位置挪到了哪里,再暴力还原。再看第 n − 1 n-1 n−1 次把 n − 1 n-1 n−1 从第 n − 1 n-1 n−1 个位置挪到了哪里,以此类推。
- 在还原时,可以 O ( n ) O(n) O(n) 的方法交换前半部分移动的一段和后半部分被挤到后面的一段。
- 注意若第
i
i
i 次发现
i
i
i 还在第
i
i
i 个位置,则说明这次没有移动,将
d[i] = 0
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e3 + 5;
const db eps = 1e-10;
int cas, n, m, a[N], d[N], tmp[N];
void change(int x, int last){
rep(i, 1, last - x) tmp[i] = a[i + x];
rep(i, last - x + 1, last) tmp[i] = a[i - (last - x)];
rep(i, 1, last) a[i] = tmp[i];
}
int main(){
cin >> cas;
while(cas--){
cin >> n;
rep(i, 1, n) cin >> a[i];
per(i, n, 1){
int pos = 0;
rep(j, 1, n){
if(a[j] == i){
d[i] = j;
break;
}
}
if(d[i] == i) d[i] = 0;
else change(d[i], i);
}
rep(i, 1, n) cout << d[i] << " ";
cout << endl;
}
}
/*
3
6
3 2 5 6 1 4
3
3 1 2
8
5 8 1 3 2 6 4 7
0 1 1 2 0 4
0 0 1
0 1 2 0 2 5 6 2
*/
1650E. Rescheduling the Exam
-
思路:若第 i i i 场考试前的休息日子最短,则只有两种选择,即移动第 i i i 场考试或第 i − 1 i-1 i−1 场考试
-
当移动一场考试时,不妨设为第 i i i 场。将这场拿走,则其只有两个地方可以放,
- 最后一天:即第
d
d
d 天,这样增加一个间隔
d - a[last] - 1
(注意不是d - a[n] - 1
,因为有可能移动的就是 a [ n ] a[n] a[n]) - 剩余间隔天数中的最中间一天:这样增加间隔
(maxn - 1) / 2
最后和当前最小间隔取最小值。
- 最后一天:即第
d
d
d 天,这样增加一个间隔
-
于是分别删去第 i i i 场考试和第 i − 1 i-1 i−1 场考试,求出剩余 n − 1 n-1 n−1 场的间隔中的最大值和最小值,代入计算。
-
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e5 + 5;
const db eps = 1e-10;
int cas, n, d, a[N], MIN, posmin, ans;
VI schedule;
int calc(){
int minn = INF, maxn = 0;
rep(i, 1, n - 1){
minn = min(minn, schedule[i] - schedule[i - 1] - 1);
maxn = max(maxn, schedule[i] - schedule[i - 1] - 1);
}
return min(minn, max(d - schedule.back() - 1, (maxn - 1) / 2));
}
int main(){
// freopen("1.in","r",stdin);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin >> cas;
while(cas--){
cin >> n >> d;
a[0] = 0, MIN = INF, posmin = 0;
rep(i, 1, n){
cin >> a[i];
int d = a[i] - a[i - 1] - 1;
if(MIN > d){
MIN = d;
posmin = i;
}
}
schedule.clear();
//去掉posmin
rep(i, 0, n){
if(i != posmin) schedule.push_back(a[i]);
}
ans = calc();
//去掉posmin - 1
if(posmin > 1){
schedule[posmin - 1] = a[posmin];
}
ans = max(ans, calc());
cout << ans << endl;
}
}
/*
9
3 12
3 5 9
2 5
1 5
2 100
1 2
5 15
3 6 9 12 15
3 1000000000
1 400000000 500000000
2 10
3 4
2 2
1 2
4 15
6 11 12 13
2 20
17 20
*/
1650G. Counting Shortcuts
-
题目:无权无向图中,从 s s s 点到 t t t 点的最短路和最短路加一的路径共有多少条。
-
思路:不妨设最短路长度为 s d i s t [ t ] sdist[t] sdist[t]
-
易证:一条满足条件的路径一定不会重复走过同一点(这样的路径长度至少为 s d i s t [ t ] + 2 sdist[t]+2 sdist[t]+2 )
-
首先可用两个 b f s bfs bfs 求出每个点到 s , t s,t s,t 点的最短距离 s d i s t [ i ] , t d i s t [ i ] sdist[i],tdist[i] sdist[i],tdist[i] 和方案数 s n u m [ i ] , t n u m [ i ] snum[i],tnum[ i] snum[i],tnum[i] 。
-
其次求最短路加一的路径数量,一定是过相邻两点 i , j i,j i,j,满足
s d i s t [ i ] + t d i s t [ j ] + 1 = s d i s t [ t ] + 1 sdist[i]+tdist[j]+1=sdist[t]+1 sdist[i]+tdist[j]+1=sdist[t]+1
由于一条这样的路径可能被重复计算,可以只记录 s d i s t [ i ] = s d i s t [ j ] sdist[i]=sdist[j] sdist[i]=sdist[j] 是的情况。
-
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e5 + 5;
const db eps = 1e-10;
const int mod = 1e9 + 7;
int cas, n, m, s, t, a[N];
ll sdist[N], snum[N], tdist[N], tnum[N], ans;
VI edge[N];
void bfs(int src, ll dist[], ll num[]){
queue<int> q;
rep(i, 1, n) dist[i] = -1, num[i] = 0;
q.push(src), dist[src] = 0, num[src] = 1;
while(q.size()){
int now = q.front(); q.pop();
for(auto nxt : edge[now]){
if(dist[nxt] == -1){
dist[nxt] = dist[now] + 1;
q.push(nxt);
}
if(dist[nxt] == dist[now] + 1){
(num[nxt] += num[now]) %= mod;
}
}
}
}
int main(){
// freopen("1.in","r",stdin);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin >> cas;
while(cas--){
cin >> n >> m >> s >> t;
rep(i, 1, n) edge[i].clear();
rep(i, 1, m){
int u, v; cin >> u >> v;
edge[u].push_back(v), edge[v].push_back(u);
}
bfs(s, sdist, snum);
bfs(t, tdist, tnum);
ans = 0; //最短路
rep(i, 1, n){
for(auto j : edge[i]){
if(sdist[i] + tdist[j] == sdist[t] && sdist[i] == sdist[j]){ //次最短路
ans += snum[i] * tnum[j] % mod;
ans %= mod;
}
}
}
cout << (ans + snum[t]) % mod << endl;
}
}