◇赛时·V◇ Codeforces Round #486 Div3

又是一场历史悠久的比赛,老师拉着我回来考古了……为了不抢了后面一些同学的排名,我没有做A题


◆ 题目&解析

【B题】Substrings Sort +传送门+   [暴力模拟]

  • 题意

给出n个字符串,你需要将它们排序,使得对于每一个字符串,它前面的字符串都是它的子串(对于字符串i,则字符串 1~i-1 都是它的子串)。

  • 解析

由于n最大才100,所以 O(n3) 的算法都不会爆,很容易想到暴力模拟。

如果字符串i是字符串j的子串,则(len是长度)len[i]≤len[j]。所以我们可以把字符串按长度从小到大排序,然后n2判断每一个字符串前面的字符串是不是它的子串,如果都是,则直接输出,否则就是"NO"。

这里找子串我用的是 string 的 substr 取子串,对于字符串 i,j (len[j]≤len[i] && i≠j),找 i 中长度为 len[j] 的子串,如果有一个子串等于 j ,则 j 是 i 的子串。

我了解到还有一个 find 函数,可以直接找,但是不会用啊 ヽ(╯▽╰)ノ

  • 源代码
 /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=;
int n,len[MAXN+];
string str[MAXN+];
bool cmp(string A,string B){return A.length()<B.length();}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
cin>>str[i];
sort(str,str+n,cmp);
for(int i=;i<n;i++)
len[i]=str[i].length();
for(int i=n-;i>=;i--)
for(int j=i-;j>=;j--)
{
bool flag=false;
for(int k=;k<=len[i]-len[j];k++)
{
if(str[i].substr(k,len[j])==str[j])
{
flag=true;
break;
}
}
if(!flag) {printf("NO\n");return ;}
}
printf("YES\n");
for(int i=;i<n;i++)
cout<<str[i]<<endl;
return ;
}

【C题】Equal Sums +传送门+  [STL实现]

  • 题意

给出n个序列(序列长度以及序列元素),你要从中挑选出两个序列,并在两个序列中分别删除一个数,使得删除后两序列元素之和相等。输出你挑选的序列以及你删除的是该序列中的第几个元素。如果无法找到满足条件的序列,输出"-1"。

  • 解析

这道题最可怕的是它的数据规模,根本存不下……因此我们要学会在线处理。

所谓在线处理就是一边输入一边计算答案。我们可以发现,其实它的序列之和非常小,105是数组存得下的,于是想到储存减去一个元素的和的方法。

我们可以储存它当前输入的序列(谢天谢地),所以我们可以求出它的元素之和与每个元素之差。这里就可以用一个 "map<int,pair<int,int> >" 也就是把一个整数映射到一个整数对上。这里的整数是序列之和与它的一个元素的差,整数对就是{序列的编号,序列中被删除的元素编号}。先计算出当前序列的和sum,再枚举它的每一个元素i,计算总和减去元素i的值x,如果发现映射已经中有一个值等于x,则说明之前有一个序列总和减去它的某一个元素等于当前序列减去元素i了(也就是满足题目的条件),这个时候就可以储存答案,并标记答案已经找到。然后把当前的每一个值存入映射中(不需要担心会把之前的映射值覆盖,反正都是输出任意解),要注意不能一边查找映射中是否存在答案,一边将当前序列的值存入映射中,因为这样可能找到同一个序列!

  • 源代码
 /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
int QuickRead(int &x)
{
int a=,b=;char c=getchar();
while(!(''<=c && c<='')) b=c=='-'?-:b,c=getchar();
while(''<=c && c<='') a=a*+c-'',c=getchar();
return x=a*b;
}
const int MAXN=int(2e5);
int n,num[MAXN+];
map<int,pair<int,int> > vis;
int main()
{
bool flag=false;
scanf("%d",&n);
for(int i=;i<n;i++)
{
int len,sum=;QuickRead(len);
for(int j=;j<len;j++) sum+=QuickRead(num[j]);
if(flag) continue;
for(int j=;j<len;j++)
if(vis.count(sum-num[j]))
{
flag=true;
printf("YES\n");
printf("%d %d\n",vis[sum-num[j]].first,vis[sum-num[j]].second);
printf("%d %d\n",i+,j+);
break;
}
if(flag) continue;
for(int j=;j<len;j++)
vis[sum-num[j]]=make_pair(i+,j+);
}
if(!flag) printf("NO\n");
return ;
}

【D题】 Points and Powers of Two +传送门+  [数学推导]

  • 题意

给出n个数,你需要从中选出尽量多的数加入集合 A,使得集合 A 中的每个元素与其他元素的差的绝对值为2的幂,输出集合A的大小,并输出集合A(多解输出任意一个)。

  • 解析

设集合A为 {a1,a2,...ap} (ai<ai+1),且 ai+1 - ai=2x[i](1≤i<p)。则 ai+2 - a= 2x[i] + 2x[i+1],而我们知道 ai+2 - ai 也是2的幂,什么时候 2x[i] + 2x[i+1] 是2的幂呢?其实就是 x[i]=x[i+1] 的时候!所以我们可以得到集合A中相邻元素的差是相等的。又因为差相等,所以集合A最多有3个元素,且都不相等(=_= 自己找个数据试试吧)。

我们可以用set储存n个数中出现的数,然后枚举一个数 num[i],再枚举2的幂 2j,然后再枚举集合A的长度 k,判断set中(即n个数中)是否有k个元素满足相邻之差为 2j,若有,则更新答案 。

注意:如果没有连续的元素满足上述条件,则输出n个数的任意一个(因为一个数没有差)。

  • 源代码
 /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
using namespace std;
const int MAXN=int(2e5);
int n;
long long num[MAXN+];
set<long long> vis;
vector<long long> _2;
vector<long long> ans;
long long ANS;
int main()
{
long long X=;
while(X<=2e9)
_2.push_back(X),X*=;
_2.push_back(X);
scanf("%d",&n);
for(int i=;i<n;i++)
scanf("%lld",&num[i]),vis.insert(num[i]);
for(int i=;i<n;i++)
for(int j=;j<_2.size();j++)
for(int k=;k<=;k++)
if(vis.count(num[i]+k*_2[j]))
{
int f=k+;
if(f>ans.size())
{
ans.clear();ANS=j;
for(int g=;g<f;g++)
ans.push_back(num[i]+g*_2[j]);
}
}
else
break;
if(ans.empty())
{
printf("%d\n%lld\n",,num[]);
}
else
{
printf("%d\n",ans.size());
for(int i=;i<ans.size();i++)
i? printf(" %lld",ans[i]):printf("%lld",ans[i]);
printf("\n");
}
return ;
}

【E题】 Divisibility by 25 +传送门+  [代码实现]

  • 题意

给出一个数n,你可以选择n的相邻两个数位对其进行交换,最后使得n能够被25整除,但是在每一次交换后不能够出现前导零(输入时保证没有前导零)。如果可以实现,则输出最少需要交换多少次,反正输出-1。

  • 解析

25是一个 special 的数字,它的倍数末尾两位为 00,25,50或75 。所以我们可以把问题转换为——交换n的相邻两位,使得n的末尾为 00,25,50或75。

四种情况不多,可以分类讨论,计算每一种情况的答案取最小值。我们可以直接用字符串存数n。

写一个 Find(x,sta) 表示n中从第sta个数位开始,数字x第一次出现的位置(没有出现返回-1)。我们可以找到 0,2,5,7 在n中第一次出现的位置。

先是00的情况。我们再找一遍第一个0的后面还有没有0(也就是有没有两个0),然后将离个位近的0移到个位,另一个移到十位,因为数据保证没有前导零,所以这种情况不需要防止前导零以及两个数字的位置。

然后判断25的情况。首先确定2,5是否都存在,则将5移到个位,2移到十位,需要考虑前导零、两个数字最初的位置。50,75 与 25相同。

下面是关于 25,50,75 的一些细节:

如果两个数字最初的先后顺序与我们要求的不同(比如25的情况,最初5在2前面)。则两个数字一定有一次操作使他们互换,使得他们顺序正确。所以操作数加1。

对于前导零,我们先找到除开我们需要移动的两个位置的数的不为零的数的位置(比如52102中找25,找到的第一个不为零的数为2(千位))。如果在第一个0出现之前,则不用管,否则把它移到第一个0前面,再将目标移到末尾。(详见Zero()函数)

  • 源代码
 /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=int(1e9);
char str[];
int len,ans=INF;
int pos[];
int Find(char x,int sta)
{
for(int i=sta;i>=;i--)
if(str[i]==x)
return i;
return -;
}
int Zero(int A,int B)
{
if(len==) return ;
int cnt=;
for(int i=;i<=len;i++)
{
if(str[i]!='' && A!=i && B!=i)
return i--cnt;
if(i==A || i==B) cnt++;
}
return INF+;
}
int main()
{
scanf("%s",str+);
len=strlen(str+);
pos[]=Find('',len);
pos[]=Find('',len);
pos[]=Find('',len);
pos[]=Find('',len);
if(pos[]!=- && (pos[]=Find('',pos[]-))!=-)
ans=min(ans,len--pos[]+len-pos[]);
if(pos[]!=- && pos[]!=-)
ans=min(ans,len-pos[]+len--pos[]+(pos[]>pos[])+Zero(pos[],pos[]));
if(pos[]!=- && pos[]!=-)
ans=min(ans,len-pos[]+len--pos[]+(pos[]>pos[])+Zero(pos[],pos[]));
if(pos[]!=- && pos[]!=-)
ans=min(ans,len-pos[]+len--pos[]+(pos[]>pos[])+Zero(pos[],pos[]));
printf("%d\n",ans==INF? -:ans);
return ;
}

【F题】 Rain and Umbrellas +传送门+  [动态规划]

  • 题意

你在一条数轴上的0点,需要走到a点。在0~a的位置中,有n个区间在下雨(保证区间不相交),经过这些区间时必须打伞。有m个位置放了伞(同一个位置可能有多把伞),伞有不同的重量,你可以捡起这把伞,而你每行走一个单位长度,你花费的体力是你捡起的伞的重量。你可以在任意处扔下伞(除非你正在下雨的区间内),且可以瞬间丢下伞或者拿起伞。求你从0走到a的最小花费体力。

  • 解析

这道题考虑的东西比较多,肯定是动态规划。而且区间不多,区间总长度也不大,连离散化也不用。

定义 dp[i] 为走到 i 时的最小花费体力,rain[i] 表示i位置是否在下雨,unb[i] 表示 i 位置的伞的最小重量(没有伞则赋值为极大值)。

如果第 i-1 个位置没有下雨,则我们可以延续第 i-1 个位置的状态,即 rain[i-1]==false 时,dp[i]=dp[i-1];否则我们往前找到一个有伞的位置j,设我们在j处拿上(或者换上伞),然后走到i处,则走了(i-j)个单位距离,便可以计算 j->i 的体力花费,即 dp[i]=min{dp[j]+(i-j)*unb[j]} 。

最后答案即是 dp[a]。

注意这里描述的下雨的区间其实是一个左开右闭区间,样例1如下图:

也就是说i~i+1的雨可以看成i点的雨,也就压缩成了左开右闭区间。

  • 源代码
 /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXM=;
typedef long long ll;
int goal,n_rain,n_unb;
bool rain[MAXM+];
int unb[MAXM+];
ll dp[MAXM+];
int main()
{
scanf("%d%d%d",&goal,&n_rain,&n_unb);
fill(dp,dp+MAXM+,(ll)1e18);
fill(unb,unb+MAXM+,(int)1e9);
for(int i=;i<n_rain;i++)
{
int l,r;scanf("%d%d",&l,&r);
if(l>r) swap(l,r);
for(int j=l;j<r;j++)
rain[j]=true;
}
for(int i=;i<n_unb;i++)
{
int pos,wgt;scanf("%d%d",&pos,&wgt);
unb[pos]=min(unb[pos],wgt);
}
dp[]=;
for(int i=;i<=goal;i++)
if(!rain[i-])
dp[i]=dp[i-];
else
for(int j=i-;j>=;j--)
if(unb[j]!=1e9)
dp[i]=min(dp[i],dp[j]+(i-j)*unb[j]);
if(dp[goal]==1e18) printf("-1\n");
else printf("%lld\n",dp[goal]);
return ;
}

The End

Thanks for reading!

- Lucky_Glass

(Tab:如果我有没讲清楚的地方可以直接在邮箱lucky_glass@foxmail.com email我,在周末我会尽量解答并完善博客~)

【赛时总结】◇赛时·V◇ Codeforces Round #486 Div3的更多相关文章

  1. Codeforces Round #486 (Div. 3) D. Points and Powers of Two

    Codeforces Round #486 (Div. 3) D. Points and Powers of Two 题目连接: http://codeforces.com/group/T0ITBvo ...

  2. Codeforces Round #486 (Div. 3) F. Rain and Umbrellas

    Codeforces Round #486 (Div. 3) F. Rain and Umbrellas 题目连接: http://codeforces.com/group/T0ITBvoeEx/co ...

  3. Codeforces Round #486 (Div. 3) E. Divisibility by 25

    Codeforces Round #486 (Div. 3) E. Divisibility by 25 题目连接: http://codeforces.com/group/T0ITBvoeEx/co ...

  4. Codeforces Round #486 (Div. 3) A. Diverse Team

    Codeforces Round #486 (Div. 3) A. Diverse Team 题目连接: http://codeforces.com/contest/988/problem/A Des ...

  5. CodeForces Round #527 (Div3) C. Prefixes and Suffixes

    http://codeforces.com/contest/1092/problem/C Ivan wants to play a game with you. He picked some stri ...

  6. CodeForces Round #527 (Div3) B. Teams Forming

    http://codeforces.com/contest/1092/problem/B There are nn students in a university. The number of st ...

  7. CodeForces Round#480 div3 第2场

    这次div3比上次多一道, 也加了半小时, 说区分不出1600以上的水平.(我也不清楚). A. Remove Duplicates 题意:给你一个数组,删除这个数组中相同的元素, 并且保留右边的元素 ...

  8. CODEFORCES ROUND#624 DIV3

    这次比赛从名字就可以看出非常水,然鹅因为第一次打codeforces不太熟悉操作只来的及做签到题(还错了一次) A,B,C都是签到题考点思维就不写了 D题 https://codeforces.ml/ ...

  9. Codeforces Round #486 (Div. 3)-B. Substrings Sort

    B. Substrings Sort time limit per test 1 second memory limit per test 256 megabytes input standard i ...

随机推荐

  1. 《nginx 三》实现nginx的动态负载均衡——实战

    Http动态负载均衡 什么是动态负载均衡 传统的负载均衡,如果Upstream参数发生变化,每次都需要重新加载nginx.conf文件, 因此扩展性不是很高,所以我们可以采用动态负载均衡,实现Upst ...

  2. php删除服务器所有session踢掉所有在线用户linux

    注意:如果要删除服务器上所有session,重启php服务是解决不了问题的,php的session是持久化的. 有效解决办法: 删除 /tmp 下的所有文件(默认php的session文件是在/tmp ...

  3. JS URL解析

    function urlParse(url) { try { var hostexp = /http\:\/\/([^\/]+)/; var hoststr = url.match(hostexp)[ ...

  4. Vue.js - Day1

    什么是Vue.js Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于We ...

  5. bai_du 采集代码(已过期)

    <?php $url = "http://www.baidu.com/s?wd=site:www.xxxxxx.com+inurl:hot&tn=baidulaonian&am ...

  6. 【菜鸟学Linux】Cron Job定期删除Log(日志)文件

    以前一直做Windows开发,近期的项目中要求使用Linux.作为小菜鸟一枚,赶紧买了一本经典书<鸟哥的Linux私房菜>学习.最近刚好有一个小任务 - 由于产品产生的Log很多,而且增长 ...

  7. 二种方法安装卸载Windows服务的命令

    第一种方法:通过Dos命令安装系统服务1. 开始 运行输入 cmd 进入dos窗口2. cd命令进入到C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727目录下, ...

  8. 《深入理解Java 7核心技术与最佳实践》读书笔记(2) Java语言动态性引言

    Java语言是一种静态类型的编程语言.静态类型的含义是指在编译时进行类型检查.Java源代码中的每个变量的类型都要显式地进行声明.所有变量.方法的参数和方法返回值的类型在程序运行之前就必须是已知的.J ...

  9. 怎样解决putty终端乱码的方法

    原文地址:https://jingyan.baidu.com/article/3aed632e5f00ae701080913a.html?qq-pf-to=pcqq.c2c 终端输入:echo $LA ...

  10. HCNA配置浮动静态路由

    1.拓扑图 2.配置IP R1 Please press enter to start cmd line! ############ <Huawei> Dec ::-: Huawei %% ...