Codeforces Round #677 (Div. 3)
Posted dyhaohaoxuexi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #677 (Div. 3)相关的知识,希望对你有一定的参考价值。
写在前面
最近开始刷CF,今天第一次补完完整的一套CF,总结一下吧,发现做过的题不赶紧总结写题解就容易忘了。
立个flag 争取每天更新一篇CF题解
A. Boring Apartments
模拟题,模拟就完了
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int sum, a[100005];
int main(){
for (int i = 1; i <= 9;i++){
for (int j = 1; j <= 4;j++){
sum += j;
int temp = i;
for (int k = 1; k < j;k++){
temp = temp * 10 + i;
}
a[temp] = sum;
}
}
int n;
cin >> n;
while(n--){
int x;
cin >> x;
cout << a[x] << endl;
}
return 0;
}
B. Yet Another Bookshelf
大意: 1代表有书,0代表没有书,对于每次操作,可以将一段连续的1整体向左或向右移动,问将全部的1移到一起最少要多少步
思路: 直接计算中间的0有多少个就行
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t, n;
int main(){
cin>>t;
while(t--){
cin>>n;
int x,flag=0,cnt=0,cnt0=0;
for (int i = 0; i < n;i++){
cin >> x;
if(x==1&&flag==0){
flag = 1;
}
else if(flag==1&&x==0){
cnt++;
cnt0++;
}
else if(x==1&&flag==1){
cnt0 = 0;
}
}
cout << cnt - cnt0 << endl;
}
return 0;
}
C. Dominant Piranha
大意: 给一个数组a,对于任意一个元素a[i],如果它大于相邻的元素,可以将这个较小的元素吃掉,然后自己的值增加1,问能否找到一个元素,不断吃掉相邻的元素,最终吃掉除自己之外的整个数组。
思路: 对于一个数组,如果所有的元素都相同,那么必然找不到可以吃的元素;否则就找到最大的元素,如果最大的元素有多个,则找到存在较小相邻元素的那个即可,因为这样它可以通过吃掉这个较小的元素,从而成为唯一的最大的元素。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int t, n, maxn, anss,x,a[N],flag=0;
int main(){
cin>>t;
while(t--){
cin >> n;
maxn = -1;
for (int i = 1; i <= n;i++){
cin >> a[i];
maxn = max(maxn, a[i]);
}
anss = -1;
for (int i = 1; i <= n;i++){
if(i==1){
if(a[i]==maxn&&a[i+1]!=a[i]){
anss = i;
break;
}
}
else if(i==n){
if(a[i]==maxn&&a[i-1]!=a[i]){
anss = i;
break;
}
}
else{
if(a[i]==maxn&&(a[i-1]!=a[i]||a[i+1]!=a[i])){
anss = i;
break;
}
}
}
cout << anss << endl;
}
return 0;
}
D. Districts Connection
大意: 给出n个数,其中 (a_i)代表第i个街区属于第(a_i)个帮派,相同帮派之间的街区不能直连,问能否得到符合条件的树,使得所有的街区都联通
思路: 直接构造即可,因为不同的街区之间不能直连,所以将所有和第一个街区所属帮派不同的街区和第一个街区相连,然后将和第一个街区的帮派相同的街区与 和第一个街区相连的一个街区 相连即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t, n,other,a[5005];
vector<int> anss[2];
int main(){
cin>>t;
while(t--){
cin >> n;
other = 0;
anss[0].clear();
anss[1].clear();
for (int i = 1; i <= n;i++){
cin >> a[i];
if(a[i]!=a[1]){
anss[0].push_back(i);
other = i;
}
}
if(other==0){
cout << "NO" << endl;
}
else{
cout << "YES" << endl;
for (int i = 2; i <= n;i++){
if(a[i]==a[1]){
anss[1].push_back(i);
}
}
for (int i = 0; i < anss[0].size();i++){
cout << 1 << ‘ ‘ << anss[0][i] << endl;
}
for (int i = 0;i<anss[1].size();i++){
cout << other << ‘ ‘ << anss[1][i] << endl;
}
}
}
return 0;
}
E. Two Round Dances
大意: 将n个人分成两组,对于每一组都是一个环,问有多少种不同的分法,注意对于一个环来说 1234 和 4123是相同的
思路: 一开始不会,搜了一下才知道这是圆排列数
圆排列的定义为: 有一组元素,将其排成一个圆,这种排列叫做圆排列或项链排列。
一般地,有m个元素作圆排列,其计算公式为 ((m-1)!)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
long long anss=1,n;
int main(){
cin>>n;
for (int i = (n / 2) + 1; i <= n; i++){
anss *= i;
}
for (int i = 1; i <= (n / 2);i++){
anss /= i;
}
for (int i = (n / 2) - 1; i >= 1;i--){
anss *= i;
}
for (int i = (n / 2) - 1; i >= 1;i--){
anss *= i;
}
cout << anss / 2 << endl;
return 0;
}
F. Zero Remainder Sum
大意: 每行只能选择(leftlfloorfrac{m}{2}
ight
floor)个元素,问选出的元素和满足能被&k&整除的最大值是多少
思路: 看题解说是经典dp问题,可是我不会qwq....
(dp[x][y][cnt][rem])代表当前位置为((x,y)) 已经选取了(cnt)个元素且余数是(rem)
那么
(dp[1][1][0][0]=0)代表最初的位置
(dp[nx][ny][cnt][rem] = max(dp[nx][ny][cnt][rem],dp[x][y][cnt][rem])) 表示不取当前元素
(dp[nx][ny][cnt + 1][(rem+a_{ij})\%k] = max(dp[nx][ny][cnt+1][(rem+a_{ij})\%k],dp[x][y][cnt][rem] + a_{ij})) 表示取当前元素
然后一行一行的扫过去就行,最后输出(dp[n+1][1][0][0])
#include<bits/stdc++.h>
using namespace std;
const int N = 70 + 5;
int a[N][N], dp[N][N][N][N], n, m, k;
int main(){
cin >> n >> m >> k;
for (int i = 1; i <= n;i++){
for (int j = 1; j <= m;j++){
cin >> a[i][j];
}
}
memset(dp, -1, sizeof(dp));
dp[1][1][0][0] = 0;
for (int i = 1; i <= n;i++){
for (int j = 1; j <= m;j++){
for (int cnt = 0; cnt <= (m / 2);cnt++){
for (int rem = 0; rem < k;rem++){
if(dp[i][j][cnt][rem]==-1){
continue;
}
int ii=i, jj=j+1;
if(j==m){
jj = 1;
ii = i + 1;
}
if(ii==i){
dp[ii][jj][cnt][rem] = max(dp[ii][jj][cnt][rem], dp[i][j][cnt][rem]);
}
else{
dp[ii][jj][0][rem] = max(dp[ii][jj][0][rem], dp[i][j][cnt][rem]);
}
if((cnt+1)<=m/2){
if(ii==i){
dp[ii][jj][cnt+1][(rem+a[i][j])%k] = max(dp[ii][jj][cnt+1][(rem+a[i][j])%k], dp[i][j][cnt][rem]+a[i][j]);
}
else{
dp[ii][jj][0][(rem+a[i][j])%k] = max(dp[ii][jj][0][(rem+a[i][j])%k], dp[i][j][cnt][rem]+a[i][j]);
}
}
}
}
}
}
cout << max(0, dp[n + 1][1][0][0]) << endl;
return 0;
}
G. Reducing Delivery Cost
大意: n个点m个边,可以把其中一个边权值变为0,给出k对起点与终点,问这k对点的距离和最小可以是多少
思路: 首先跑n次堆优化的dijsktra算法,由于数据较小,可以枚举被删除的边,然后暴力算就行
注意删除一条边((u,v))后,需要用(min(dis[sx][tx], min(dis[sx][u] + dis[v][tx], dis[sx][v] + dis[u][tx])))来更新
板子是队友的板子,看了好久才明白...
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
int n, m, k;
typedef pair<int, int> PII;
int e[2*N], ne[2*N],w[2*N], h[N], idx,dis[N][N], st[N],mp[N][N],anss=0x3f3f3f3f;
struct node
{
int a, b, c;
};
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
// 堆优化版dijksyta
int dijkstra(int sx) {
memset(dis[sx], 0x3f, sizeof dis[sx]); // 初始化距离为无穷
memset(st, 0, sizeof st);
priority_queue<PII, vector<PII>, greater<PII> > q; // 定义一个按照距离从小到大排序的优先队列,第一维:距离,第二维:点
dis[sx][sx] = 0; // 一开始源点距离为0
q.push({0, sx}); // 把源点信息放入队列
while (q.size()) { // 每个点只出入队列一次
auto t = q.top();
q.pop();
int distance = t.first, ver = t.second; // 最小距离和相对应的点
if (st[ver]) continue; // 这个操作保证每个点只出入队一次,因为队列里面可能会出现{dis1[3], 3}, {dis2[3], 3}的情况,这样保证dis1[3]<dis2[3]时,3号点只进出入队一次
st[ver] = 1; // 标记,因为dijkstra的贪心策略保证每个点只需要进出队一次
for (int i = h[ver]; ~i; i = ne[i]) { // 遍历ver的邻接点
int j = e[i];
if (dis[sx][j] > distance + w[i]) {
dis[sx][j] = distance + w[i];
q.push({dis[sx][j], j}); // 这里不需要判断st,因为一旦更新发现更小必须放入队列
}
}
}
}
struct road
{
int u, v, w;
};
vector<struct road> r;
vector<pair<int, int> >q;
int main(){
cin >> n >> m >> k;
memset(h, -1, sizeof h);
//memset(mp, 0x3f, sizeof mp); // 初始更新每个点间距离为无穷远
for (int i = 0; i < m;i++){
int x, y, z;
struct road node;
cin >> node.u >> node.v >> node.w;
r.push_back(node);
add(node.u, node.v, node.w);
add(node.v, node.u, node.w);
//mp[node.u][node.v] = min(node.w,mp[node.u][node.v]);
}
for (int i = 1; i <= n;i++){
dijkstra(i);
}
for (int i = 0; i < k; i++)
{
int sx, tx;
cin >> sx >> tx;
q.push_back({sx, tx});
}
for (int i = 0; i < r.size();i++){
int temp = 0;
for (int j = 0; j < q.size();j++){
int sx = q[j].first;
int tx = q[j].second;
temp += min(dis[sx][tx], min(dis[sx][r[i].u] + dis[r[i].v][tx], dis[sx][r[i].v] + dis[r[i].u][tx]));
}
anss = min(temp, anss);
}
cout << anss << endl;
return 0;
}
以上是关于Codeforces Round #677 (Div. 3)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #677 (Div. 3)
Codeforces Round #677 (Div. 3)
Codeforces Round #677 (Div. 3)D. Districts Connection
Codeforces Round #436 E. Fire(背包dp+输出路径)