当前位置: 首页 > news >正文

Codeforces Round 545 (Div. 2)


layout: post
title: Codeforces Round 545 (Div. 2)
author: "luowentaoaa"
catalog: true
tags:
mathjax: true
- codeforces
- 贪心
- 数学
- floyd判环


传送门

A - Sushi for Two(签到)

题意

在一个 01 序列中找出长为偶数的连续的一段使得它前一半和后一半内部分别相同,而前一半和后一半不同。

思路

纸上模拟了一下然后预处理 赛后发现太麻烦了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=3e5+50;
const int inf=1e9;
typedef unsigned long long ull;
int a[maxn];
int one[maxn];
int zero[maxn];
int num[maxn];
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]==1)one[i]++;
        else zero[i]++;
    }
    for(int i=1;i<=n;i++){
        if(one[i])one[i]=one[i-1]+1;
        if(zero[i])zero[i]=zero[i-1]+1;
     //   cout<<one[i]<<" "<<zero[i]<<endl;
    }
    for(int i=n;i>=1;i--){
        if(one[i])one[i]=max(one[i],one[i+1]);
        if(zero[i])zero[i]=max(zero[i],zero[i+1]);
    }
  //  cout<<endl;
    int ans=0;
    for(int i=1;i<=n;i++){
     //   cout<<one[i]<<" "<<zero[i]<<endl;
        ans=max(ans,min(one[i-1],zero[i]));
        ans=max(ans,min(zero[i-1],one[i]));
    }
    cout<<ans*2<<endl;

    return 0;
}

B - Circus (数学,推公式)

题意

有 n个人,需要把它们分成两组,每组恰好 2/n 个人。每个人可能会技能 11或技能 2,一个人可能会其中一种、两种都会或什么都不会。

要求第一组中会技能 1的人数恰好等于第二组中会技能 2 的人数,输出方案

2<=n<=5000

思路

比赛在纸上写了两张纸都没写出来的题

参考https://www.cnblogs.com/wjyyy/p/cf1138.html

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=2e5+50;
const ll inf=1e17;
typedef unsigned long long ull;
vector<int>v[5];
char s[maxn],t[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%s%s",s+1,t+1);
    for(int i=1;i<=n;i++){
        v[s[i]-'0'+(t[i]-'0')*2].push_back(i);
    }
    int a=v[0].size(),b=v[1].size(),c=v[2].size(),d=v[3].size(),h=n/2;
    for(int i=0;i<=d;i++){
        if(d-i+c>h||i+b>h)continue;
        if(i<d-i){
            if(b<d-2*i)continue;
            for(int j=0;j<i;j++)
                printf("%d ",v[3][j]);
            h-=i;
            for(int j=0;j<d-2*i;j++)
                printf("%d ",v[1][j]);
            h-=d-2*i;
            for(int j=0;j<c;j++)
                printf("%d ",v[2][j]);
            h-=c;
            for(int j=0;j<h;j++)
                printf("%d ",v[0][j]);
            return 0;
        }
        else{
            if(c<2*i-d)continue;
            c-=2*i-d;
            for(int j=0;j<i;++j)
                printf("%d ",v[3][j]);
            h-=i;
            for(int j=0;j<c;++j)
                printf("%d ",v[2][j]);
            h-=c;
            for(int j=0;j<h;++j)
                printf("%d ",v[0][j]);
            return 0;
        }
    }
    printf("-1");
    return 0;
}

C - Skyscrapers (离散化+贪心)

题意

有一个 n×m的矩阵 aijaij。

对于每个 (i,j)1≤i≤n1≤i≤n),你把第 i行和第 j 列单独抽出,这样就有 n+m−1 个数被你抽出。

你可以对这些数重新标号为正整数,但是要满足第 i行所有数的大小关系不变,第 j列所有数的大小关系不变(两个限制相互独立)。

满足这个限制下,你希望最大的标号尽量小,对每个 (i,j) 求出这个最小的最大标号。

思路

因为行大小关系不变,列大小关系不变,我们考虑分别离散化每行每列,并统计每个数在行内和列内的排名。

然后贪心取个最大的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=3e5+50;
const int inf=1e9;
typedef unsigned long long ull;
vector<int>ve;
int a[1500][1500];
vector<int>h[1500];
vector<int>l[1500];
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            h[i].push_back(a[i][j]);
            l[j].push_back(a[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        sort(h[i].begin(),h[i].end());
        h[i].erase(unique(h[i].begin(),h[i].end()),h[i].end());
    }

    for(int i=1;i<=m;i++){
        sort(l[i].begin(),l[i].end());
        l[i].erase(unique(l[i].begin(),l[i].end()),l[i].end());
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int A=h[i].size(),aa=lower_bound(h[i].begin(),h[i].end(),a[i][j])-h[i].begin()+1;
            int B=l[j].size(),b=lower_bound(l[j].begin(),l[j].end(),a[i][j])-l[j].begin()+1;
            cout<<(aa<b?max(B,b+A-aa):max(A,aa+B-b))<<" ";
        }
        cout<<endl;
    }
    return 0;
}

D - Camp Schedule (KMP 贪心)

题意

有两个 01 串 s 和 t,要求重新排列 s 使得 t 在重新排列后的 s 中出现次数尽量多(位置相交的出现也算)。

思路

一眼KMP 求出T的next数组,然后贪心重叠匹配 关键是可以位置相交

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+50;
const int inf=1e9;
typedef unsigned long long ull;
char s[maxn];
char t[maxn];
void kmp_pre(char x[],int m,int next[]){
    int i,j;
    j=next[0]=-1;
    i=0;
    while(i<m){
        while(-1!=j&&x[i]!=x[j])j=next[j];
        next[++i]=++j;
    }
}
int mynext[maxn];
int kmp(char x[],int m,char y[],int n){
    int i,j;
    int ans=0;
    kmp_pre(x,m,mynext);
    i=j=0;
    while(i<n){
        while(-1!=j&&y[i]!=y[j])j=mynext[j];
        i++;j++;
        if(j>=m){
            ans++;
            j=mynext[j];
        }
    }
    return j;
}
string str;

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    cin>>s;
    cin>>t;
    int lens=strlen(s);
    int a0=0,a1=0;
    for(int i=0;i<lens;i++)if(s[i]=='0')a0++;else a1++;
    int lent=strlen(t);
    kmp_pre(t,lent,mynext);
    int num=mynext[lent];
    //int num=kmp(t,lent,t,lent);
    string p="";
    int n1=0,n0=0;
    for(int i=num;i<lent;i++){
        p+=t[i];
        if(t[i]=='0')n0++;
        else n1++;
    }
    int b1=0,b0=0;
    for(int i=0;i<lent;i++){
        if(t[i]=='0')b0++;
        else b1++;
    }
    if(a0<b0||a1<b1){
        cout<<s<<endl;exit(0);
    }
    str+=t;
    a1-=b1;a0-=b0;
    while(a1>=n1&&a0>=n0){
        str+=p;
        a1-=n1,a0-=n0;
    }
    while(a1--){
        str+="1";
    }
    while(a0--){
        str+="0";
    }
    cout<<str<<endl;
    return 0;
}

E. Museums Tour

题意

给你一个国家,国家里面有n个城市 城市之间有单向边,国家一周有d天,每个城市有一个博物馆,会给你一个长度为d的字符串如果某个位置是‘1’代表这天这个城市的博物馆有开放
现在一个人再第1天从第一个城市出发,每天只能去往一个相邻城市,问这个人最多能见到多少个博物馆,假设天数无限

思路

首先我们把每个城市拆成d个城市。然后对于拆完的地图求强连通分量,再这个强连通分量中每个城市都可以互相到达,所以这个强连通分量的(不重复城市)的博物馆的数量就确定了。
然后就是把强连通分量缩点,变成一个新图,然后从第一个城市第一天开始dfs 跑DP这个新图

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=5e6+50;
const int inf=1e8;

typedef unsigned long long ull;

int n,m,d,dfn[maxn],low[maxn],st[maxn],vis[maxn];

int sz[maxn],dp[maxn],bel[maxn],Time,scc,top;

int Laxt[maxn],Next[maxn],To[maxn],cnt;

int Laxt2[maxn],Next2[maxn],To2[maxn],cnt2;

inline void add(int u,int v){
    Next[++cnt]=Laxt[u],Laxt[u]=cnt,To[cnt]=v;
}
inline void add1(int u,int v){
    Next2[++cnt2]=Laxt2[u],Laxt2[u]=cnt2,To2[cnt2]=v;
}

char s[100005][52];

inline int id(int a,int b){
    return (a-1)*d+b+1;
}

void tarjan(int u){
    dfn[u]=low[u]=++Time;
    st[++top]=u;vis[u]=1;
    for(int i=Laxt[u];i;i=Next[i]){
        int v=To[i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    int now;
    if(dfn[u]==low[u]){
        ++scc;
        do{
            vis[st[top]]=0;
            now=st[top--];
            bel[now]=scc;
        }while(now!=u);
    }

}

int dfs(int u){
    if(dp[u])return dp[u];
    int ans=0;
    for(int i=Laxt2[u];i;i=Next2[i]){
        int v=To2[i];
        ans=max(ans,dfs(v));
    }
    return dp[u]=ans+sz[u];
}

int main()
{
    int u,v;
    scanf("%d%d%d",&n,&m,&d);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        for(int j=0;j<d;j++)
            add(id(u,j),id(v,(j+1)%d));
    }
    for(int i=1;i<=n;i++)scanf("%s",s[i]);

    for(int i=1;i<=n*d;i++)
        if(!dfn[i])tarjan(i);

    for(int i=1;i<=n;i++){
        for(int j=0;j<d;j++){
            int x=id(i,j);
            if(s[i][j]=='1'&&vis[bel[x]]!=i)
                vis[bel[x]]=i,sz[bel[x]]++;
            for(int k=Laxt[x];k;k=Next[k]){
                int v=To[k];
                if(bel[x]!=bel[v])
                    add1(bel[x],bel[v]);
            }
        }
    }

    printf("%d\n",dfs(bel[1]));
    return 0;
}

F - Cooperative Game (Floyd判环,龟兔赛跑算法)

题意

一个条路 连着一个环,路和环的连接点定义为终点

十个人 每次可以移动一些人向前单项移动(参考链表)

交互题,每次选择一个人的编号让他想前移动

之后系统会告诉你现在哪些点有人,分别是谁

把所有人移动到终点就输出“done”结束

思路

裸的Floyd算法 过程完全一模一样

首先让两个人一个一次移动一步,一个一次移动两步。

然后两人会在一个点相遇。次数假设慢的人经过的距离为\(s\) 那么快的人距离为\(2s\)

\(s=m+i*l+k\) 1式

\(2s=m+j*l+k\) 2式

两式相减

\(s=(j-i)l\) 3式

带入1式
\[ s=m+i*l+k\\(j-i)l=m+i*l+k\\(j-2i)l=m+k\\(j-2i)l-k=m \]
所以当一个点开始从m走到起点时,另一个点也走到了开始的地方

我的想法

假设出发起点到环起点的距离为m,已经确定有环,环的周长为n,(第一次)相遇点距离环的起点的距离是k。那么当两者相遇时,慢指针(t)移动的总距离i = m + a * n + k,快指针(h)的移动距离为2i,2i = m + b * n + k。其中,a和b分别为t和h在第一次相遇时转过的圈数。让两者相减(快减慢),那么有i = (b - a) * n。即i是圈长度的倍数。

将一个指针移到出发起点S,另一个指针仍呆在相遇节点M处两者同时移动,每次移动一步。当第一个指针前进了m,即到达环起点时,另一个指针距离链表起点为i + m。考虑到i为圈长度的倍数,可以理解为指针从链表起点出发,走到环起点,然后绕环转了几圈,所以第二个指针也必然在环的起点。即两者相遇点就是环的起点。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=2e5+50;
const ll inf=1e17;
typedef unsigned long long ull;
int t;
void get(){
    fflush(stdout);
    scanf("%d",&t);
    for(int i=1;i<=t;i++)scanf("%*s");
}
int main()
{
    do{
        puts("next 0 1"),get();
        puts("next 0"),get();
    }while(t==3);
    do{
        puts("next 0 1 2 3 4 5 6 7 8 9"),get();
    }while(t==2);
    puts("done");
    return 0;
}

E - Train Car Selection (斜率)

题意

题意:维护一个数组,支持这些操作:

1、在前端塞入k个0。

2、在后端塞入k个0。

3、对于数组中每个数,假设它是数组的第i个,那么令它的值加上b+s(i−1)。

每次操作完后,询问数组中的最小值以及最左边的最小值的位置。

操作个数q≤300000。

思路

首先对于第一个操作之后肯定答案是1和0
对于第二个操作就需要判断,
对于第三个操作肯定不能直接加,那么如果不直接加那我们就可以把b和s存起来等到需要的时候加起来
发现这个最小值只和位置和现在的值有关。
所以我们可以用一个pair存储(位置,当前值) 然后对于操作三就相当于直接给这个加了一个函数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=5e5+50;
const int inf=1e8;

typedef unsigned long long ull;
#define pii pair<ll,ll>
#define mp make_pair
ll k,b;
pii p[maxn];
double slope(pii a,pii b){
    return 1.0*(a.second-b.second)/(a.first-b.first);
}
ll cal(pii x){
    return k*x.first+x.second+b;
}
int main()
{
    int n,m,op,x,y,tail;
    cin>>n>>m;
    p[tail=1]=mp(0,0);
    while(m--){
        cin>>op;
        if(op==1){
            cin>>x;tail=1,k=b=0;
            n+=x;
        }
        else if(op==2){
            pii now=mp(n,-(n*k+b));
            while(tail>1&&-slope(now,p[tail])>=-slope(p[tail],p[tail-1]))tail--;
            p[++tail]=now;
            cin>>x;
            n+=x;
        }
        else
            cin>>x>>y,b+=x,k+=y;
        while(tail>1&&cal(p[tail])>=cal(p[tail-1]))tail--;
        cout<<p[tail].first+1<<" "<<cal(p[tail])<<endl;
    }
    return 0;
}

转载于:https://www.cnblogs.com/luowentao/p/10514190.html

相关文章:

  • JQuery EasyUI 初始
  • 特殊字符
  • Ionic3关闭弹出页面,跳转到列表后刷新父页面
  • 【计算几何】二维凸包——Graham's Scan法
  • ArrayList中的一些小细节@JDK8
  • MySQL 连接 通过实例总结详解 笛卡尔积,自然连接,内连接,外连接
  • 前端的第一步
  • P3375 【模板】KMP字符串匹配
  • C++11并发——多线程std::thread (一)
  • unity下贴图混合(Texture Blending)
  • elasticsearch中ik词库配置远程热加载
  • OL4加载geowebcache 部署的离线切片
  • 在Net MVC中应用JsTree
  • nginx代理tcp协议连接mysql
  • markdown操作手册
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • Android交互
  • css系列之关于字体的事
  • Docker 笔记(2):Dockerfile
  • HTTP中的ETag在移动客户端的应用
  • JavaScript-Array类型
  • NSTimer学习笔记
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Redis 懒删除(lazy free)简史
  • Redis 中的布隆过滤器
  • springMvc学习笔记(2)
  • underscore源码剖析之整体架构
  • Webpack 4x 之路 ( 四 )
  • win10下安装mysql5.7
  • XForms - 更强大的Form
  • 从零开始在ubuntu上搭建node开发环境
  • 两列自适应布局方案整理
  • 如何借助 NoSQL 提高 JPA 应用性能
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 小试R空间处理新库sf
  • PostgreSQL之连接数修改
  • python最赚钱的4个方向,你最心动的是哪个?
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • #1014 : Trie树
  • #vue3 实现前端下载excel文件模板功能
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (Matlab)使用竞争神经网络实现数据聚类
  • (独孤九剑)--文件系统
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (十一)c52学习之旅-动态数码管
  • (四)Android布局类型(线性布局LinearLayout)
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)