Codeforces Round 932 (Div. 2) A-D题解

Codeforces Round 932 (Div. 2)

绪言

很菜,AB速度慢,卡在C,想DP,但是时间优化不下来,说服自己\(2\times10^3\)能过\(n^3\),D稍微简单,但是没看D,遂掉分。

A. Entertainment in MAC

给你一个字符串 \(s\) 和一个整数 \(n\) 。你可以对它进行两种运算:

  1. 将 \(s\) 的反向字符串添加到字符串 \(s\) 的末尾(例如,如果 \(s = cpm\) ,那么就变成了\(cpmmpc\) )。
  2. 将当前字符串 \(s\) 倒转(例如,如果 $s = $ cpm,则变成 \(s = mpc\))。

需要确定在进行精确的 \(n\) 操作后,可以得到的词序最小的字符串。两种操作进行顺序无所谓,但是必须进行 \(n\) 次。

Solution

才发现我做的时候看错题了,没有注意到题目中规定了n一定是偶数。我当成任意的来做了。

首先可以发现,字符串s长啥样不重要,重要的是字符串s和反字符串rs哪个字典序更小。

如果s小于rs:由于有反转操作的存在,可以花费偶数次操作次数,将字符串一直自己反转。此时如果n是偶数,则答案就是s。如果n是奇数,则我们先花费一次机会执行操作一,变成了s+rs的形式,再进行偶数次操作二,还是s+rs。显然s+rs的字典序小于rs

如果s大于rs:那么情况恰好相反。如果n是奇数,全部执行操作二,变成了rs。如果是偶数,那么花费一次操作机会执行操作二,变成rs,之后便等价与上面的s小于rs且还有奇数次操作的情况,执行操作一后再执行偶数次操作二,最后变成了rs+s

如果s等于rs,也就是说s是一个回文串。那么全部执行操作二,s不会发生任何变化。

Code

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t;cin>>t;
int n;
string str,rs;
while(t--)
{
cin>>n;
cin>>str;
rs=str;
reverse(rs.begin(),rs.end());
if(str<rs)
{
if(n&1) cout<<str<<rs<<endl;
else cout<<str<<endl;
}
if(str==rs)
{
cout<<str<<endl;
}
if(str>rs)
{
if(n&1) cout<<rs<<endl;
else cout<<rs<<str<<endl;
}
}
return 0;
}

B. Informatics in MAC

有一个长度为 \(n\) 的数组 \(a\) ,你想把它分成 \(k\) 个子段,使每个子段上的 \(\operatorname{MEX}\) 都等于相同的整数。

请找到合适的分配方案,或者确定找不到合法的方案。

数组中的 \(\operatorname{MEX}\) 是不属于该数组的最小非负整数。

不存在输出-1,否则输出一种合法的划分方式。

\(t\le10^4,n\le10^5,0\le a_i<n,\sum n \le 10^5\)

Solution

首先先将问题简化一下。如果我们能将数组划分成三段,使得三段的MEX相等,那么自然我们可以任意选择其中相邻的段合并一次,合并后的MEX不会变化。所以我们只需要求k=2,要是两段都划分不出来,自然是没有答案的。

那么怎么判断数组a能否被划分成两部分,且两部分的MEX相同呢?暴力扫描分界点,同时用两个cnt数组统计两区域的MEX即可。移动分界点时,更新左右段的MEX

Code

#define N 100010

int n;
int a[N];
int cnt1[N],cnt2[N],mex1,mex2; int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t;cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
memset(cnt1,0,sizeof(cnt1));
memset(cnt2,0,sizeof(cnt2));
mex1=mex2=0;
int k=1;//1~k,k+1~n k_max=n-1
cnt1[a[1]]++;
for(int i=2;i<=n;i++) cnt2[a[i]]++;
while(cnt1[mex1]) mex1++;
while(cnt2[mex2]) mex2++;
bool win=0;
if(mex1==mex2)
{
cout<<2<<endl;
cout<<"1 "<<k<<"\n"<<k+1<<" "<<n<<endl;
win=1;
continue;
}
for(k=2;k<=n-1;k++)
{
cnt1[a[k]]++;
while(cnt1[mex1]) mex1++;
cnt2[a[k]]--;
if(cnt2[a[k]]==0&&a[k]<mex2) mex2=a[k]; if(mex1==mex2)
{
cout<<2<<endl;
cout<<"1 "<<k<<"\n"<<k+1<<" "<<n<<endl;
win=1;
break;
}
}
if(!win)
{
cout<<"-1"<<endl;
}
}
return 0;
}

C. Messenger in MAC

有n个数对\((a_i,b_i)\),请在代价不超过l的情况下,请从中选出尽量多个数对。

选择是有顺序的,记录你的k个选择是\(p_1,p_2,...,p_k\),则代价计算公式为:

\[\sum^k_{i=1}a_{p_i}+\sum^{k-1}_{i=1}|b_{p_i}-b_{p_i+1}|
\]

也就是所有选择数对的a的和,加上相邻的b的差值的绝对值。

可以不选,也是答案的一种。

\(1\le t\le 5\cdot 10^4,1\le n\le 2000,1\le l\le 10^9,1\le a,b\le 10^9\)

\(\sum n^2\le4\cdot 10^6\)

Solution 暴力对顶堆

两个参量ab不好一起计算,我们要想办法拆开这个式子。

首先注意到,顺序对于代价的影响。如果已经选择了一个序列,那么调整顺序的时候,按照b的大小顺序选择是最佳方案。此时b的代价最小为\(max_{b_i}-min_{b_i}\)。

所以我们可以对于b排序,最佳的选择序列一定在某个区间范围内。枚举左右端点\([l,r]\),在区间内在保证\(\sum a_i+max\{b_i\}-min\{b_i\}\le l\)。

将\(\sum a_i+max\{b_i\}-min\{b_i\}\)优化为\(\sum a_i + b_r - b_l\),但是并不用保证当前的左右端点一定选择,因为例如当前区间为\([1,5]\),得出的最佳选择是\(\{2,3,5\}\),不用担心这样计算的代价\(\sum a_i + b_5 - b_1\)大于实际代价\(\sum a_i + b_5 - b_2\),因为会有区间为\([2,5]\)的时候,正确计算选择\(\{2,3,5\}\)的代价的。

在枚举左右端点后,我们使用对顶堆来贪心选择区间内的较小的\(a\)。具体来说,用大根堆存较小的\(a\),小根堆存较大的\(a\),并且计算大根堆的\(\sum a\),保证:

  • \(\sum a\le l\),否则要从大根堆转移\(a\)​到小根堆
  • 选择了尽量多的\(a\),无法再放入,即\(\sum a+小根堆.top()>l\),否则能够选择更多的\(a\)

语言上描述可能有些繁琐,但是写起来是非常暴力的。

Code

#define N 2010

int n,l;
pair<LL,LL>mess[N];
priority_queue<LL,vector<LL> ,greater<LL> >q2;
priority_queue<LL>q1;//small
int ans; int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t;cin>>t;
while(t--)
{
cin>>n>>l;
ans=0;
for(int i=1;i<=n;i++)
{
cin>>mess[i].second>>mess[i].first;
}
sort(mess+1,mess+1+n);
LL sum=0;
for(int i=1;i<=n;i++)
{
while(q1.size()) q1.pop();
while(q2.size()) q2.pop();
sum=0;
for(int j=i;j<=n;j++)
{
debug
if(q1.empty()||mess[j].second>q1.top())
{
q2.push(mess[j].second);
}
else q1.push(mess[j].second),sum+=mess[j].second; while(q2.size()&&sum+mess[j].first-mess[i].first+q2.top()<=l) sum+=q2.top(),q1.push(q2.top()),q2.pop();
while(q1.size()&&sum+mess[j].first-mess[i].first>l) sum-=q1.top(),q2.push(q1.top()),q1.pop(); ans=max(ans,(int)q1.size());
}
}
cout<<ans<<endl; }
return 0;
}

Solution 优雅DP

看到\(n\le2000\)时,我在思考这道题可能是dp。但是开始的做法还是太幼稚了,妄想着\(n^3=8\times 10^9\)能过\(4s\)​,相信cf测评机。

首先还是以\(b\)为关键字排序。

设\(dp_{i,j}\)为前\(i\)个数对中,选择了\(j\)个数对的最小代价。注意到这里的代价并是\(\sum a - min_{b}\),还没有加上\(max_b\)

更新答案:欲计算,以第\(k\)个数对\((a_k,b_k)\)结尾且一定选择它,在代价小于\(l\)的情况下所能选择最多数对,应寻找最大的\(j+1\)使得代价\(dp_{k-1,j}+a_k+b_k\le l\),此时\(ans=j\)

递推:\(dp_{i,j}=min\{dp_{i-1,j},dp_{i-1}+a_i\}\)

可以滚动数组优化掉\(dp_i\)

注意特判仅选择单个数对的情况,因为这时候\(b\)没有任何贡献。

Code

#define N 20010

int n,l;
pair<LL,LL>mess[N];
const int inf = 1000000009;
LL dp[N];
int ans;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t;cin>>t;
while(t--)
{
cin>>n>>l;
ans=0;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
cin>>mess[i].second>>mess[i].first;
}
sort(mess+1,mess+1+n);
LL a,b;
for(int i=1;i<=n;i++) if(mess[i].second<=l) ans=1; for(int i=1;i<=n;i++) dp[i]=inf;
for(int i=1;i<=n;i++)
{
a=mess[i].second;
b=mess[i].first; for(int j=0;j<=n;j++)
{
if(a+b+dp[j]<=l) ans=max(ans,j+1);
} for(int j=n;j>=1;j--)
{
dp[j]=min(dp[j],dp[j-1]+a);
}
dp[1]=min(dp[1],a-b); //for(int j=1;j<=n;j++) cout<<dp[j]<<" ";
//cout<<endl; }
cout<<ans<<endl; }
return 0;
}

D. Exam in MAC

给你一个大小为\(n\)的集合\(S\),其中元素均为不超过\(c\)的非负整数。

求满足一下条件的二元组\((x,y)\)的个数:

  • \(x,y\in \Z,0\le x\le y\le c\)
  • \(x+y\notin S,y-x\notin S\)

\(t\le 2\cdot10^4,n\le3\cdot10^5,1\le c\le10^9\)

集合是没有重复元素的。

Solution

考虑容斥。

首先当没有任何限制条件时,二元组\((x,y)|\{0\le x\le y\le c\}\)共有\(\frac{(c+1)(c+2)}{2}\)种。

接着考虑对于集合\(S\)内的某元素\(a\),对于\(x+y=a\)会产生\(\min(\lfloor\frac a2\rfloor,c-\lceil\frac a2\rceil)\)个二元组,对于\(y-x=a\)会产生\(\max(0,c-a+1)\)个二元组,并且由于a互异,这些二元组也是互异的,但是有可能\(a_i\)产生的\(x+y=a_i\)型二元组与\(a_j\)产生的\(y-x=a_j\)​型二元组有相同情况,于是需要考虑这种重复。

对于集合\(S\)中的元素\(a,b\),二元组\((x,y)\),有方程组:

\[x+y=a\\
y-x=b
\]

则可得:

\[x=\frac{a-b}2\\
y=\frac{a+b}2
\]

所以这样的\(a,b\)必定满足\((a-b),(a+b)\)都为偶数,即\(a,b\)奇偶性相同。

\(S\)中每一对奇偶性相同的\(a,b\)都会产生一对在容斥中被重复删除二元组,那么分别统计\(S\)中奇数、偶数的数量即可。

Code

#define N 300010

int n;
LL c;
LL s[N];
LL ji,ou;
LL ans;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t;cin>>t;
while(t--)
{
cin>>n>>c;
ji=ou=0;
ans=(c+1)*(c+2)/2;
for(int i=1;i<=n;i++)
{
cin>>s[i];
if(s[i]&1) ji++;
else ou++;
ans-=min((LL)floor(s[i]/2.0),c-(LL)ceil(s[i]/2.0));
ans-=max(0ll,c-s[i]+1);
}
ans+=ou*(ou-1)/2+ji*(ji-1)/2;
cout<<ans<<endl;
}
return 0;
}

小结

做题策略:看来有时候打div2还是有必要在遇到一道没思路的题目的时候再往后看一题,以及稍微瞄一眼其他题目的通过人数。

算法技能:dp还是弱了,要多练。

CF contest 1935 Round 932 (Div. 2) A-D题解的更多相关文章

  1. Codeforces Round #612 (Div. 2) 前四题题解

    这场比赛的出题人挺有意思,全部magic成了青色. 还有题目中的图片特别有趣. 晚上没打,开virtual contest打的,就会前三道,我太菜了. 最后看着题解补了第四道. 比赛传送门 A. An ...

  2. Codeforces Round #198 (Div. 2)A,B题解

    Codeforces Round #198 (Div. 2) 昨天看到奋斗群的群赛,好奇的去做了一下, 大概花了3个小时Ak,我大概可以退役了吧 那下面来稍微总结一下 A. The Wall Iahu ...

  3. Codeforces Round #672 (Div. 2) A - C1题解

    [Codeforces Round #672 (Div. 2) A - C1 ] 题目链接# A. Cubes Sorting 思路: " If Wheatley needs more th ...

  4. CodeForces Contest #1137: Round #545 (Div. 1)

    比赛传送门:CF #1137. 比赛记录:点我. 每次都自闭的 div1 啊,什么时候才能上 IM 呢. [A]Skyscrapers 题意简述: 有一个 \(n\times m\) 的矩阵 \(a_ ...

  5. CodeForces Contest #1114: Round #538 (Div. 2)

    比赛传送门:CF #1114. 比赛记录:点我. 又 FST 了. [A]Got Any Grapes? 题意简述: 有三个人,第一个人需要吃绿色葡萄至少 \(a\) 个,第二个人需要吃绿色和紫色葡萄 ...

  6. 13年7月13日CF练习 Codeforces Round #147 (Div. 2)

    这场div2可以说是我见过的比较水的一场吧.基本都是一眼题. 比赛地址http://acm.bnu.edu.cn/bnuoj/contest_show.php?cid=1836 题号是237A-237 ...

  7. CF AIM Tech Round 3 (Div. 2) D - Recover the String

    模拟 首先可以求出 0 和 1 的个数 之后按照01 10 的个数贪心安排 细节太多 错的都要哭了 #include<bits/stdc++.h> using namespace std; ...

  8. cf 20190307 Codeforces Round #543 (Div. 2, based on Technocup 2019 Final Round)

    B. Mike and Children time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  9. Codeforces Round #499 (Div. 2) D. Rocket题解

    题目: http://codeforces.com/contest/1011/problem/D This is an interactive problem. Natasha is going to ...

  10. Codeforces Round #499 (Div. 2) C Fly题解

    题目 http://codeforces.com/contest/1011/problem/C Natasha is going to fly on a rocket to Mars and retu ...

随机推荐

  1. 红日复现为什么失败之struct-046流量分析加msf特征总结

    struts2漏洞 一.指纹识别 s2的url路径组成(详见struts.xml配置文件):name工程名+namespace命名空间+atcion名称+extends拓展名 部署在根目录下,工程名可 ...

  2. java的三大版本、特性和优势

    Write Once Run AnyWhere 版本: JavaSE:标准版(桌面程序.控制台开发) JavaME:嵌入式开发(手机.家电...) JavaEE:E企业级开发(web端.服务端开发) ...

  3. centos 防火墙配置,并限制端口

    查看防火墙状态 systemctl status firewalld 如果防火墙处于停止状态,则启动它: systemctl start firewalld 并设置防火墙开机自启: systemctl ...

  4. 关于SIFT,GIFT在旋转不变性上的对比实验

    目录 关于SIFT,GIFT在旋转不变性上的对比实验 回顾 准确率测试 总结 核心代码 关于SIFT,GIFT在旋转不变性上的对比实验 这篇文章不讨论SIFT,GIFT的实现原理,只从最终匹配结果的准 ...

  5. BUUCTF---checkln(ROT加密)

    题目 dikqTCpfRjA8fUBIMD5GNDkwMjNARkUwI0BFTg== 解题 1.看见有==,尝试base64解码,但解码失败 2.尝试其它Base加密均失败 3.重新思考有等于号的加 ...

  6. 【Java】String字符串格式化

    一.前言 String.format() 作为文本处理工具,为我们提供强大而丰富的字符串格式化功能,为了不止步于简单调用 String.format("Hello %s", &qu ...

  7. 免费、快速、可靠:揭秘IsGPT如何精准检测AI内容

    随着GPT等技术的迅猛发展,不少人开始担心信息真实性和学术诚信.今天,分享一款由MIT CSAIL孵化的AI内容检测工具,看看它如何帮你快狠准地识别AI生成的文本 isgpt.org 市面上虽有不少A ...

  8. shader中的if语句优化

    可以借用lerp和step来拼接模仿语句 lerp(a,b,step(value,c))

  9. java 限流

    题记 在高并发的分布式系统中,我们都需要考虑接口并发量突增时造成的严重后果,后端服务的高压力严重甚至会导致系统宕机.为避免这种问题,我们都会为接口添加限流.降级.熔断等能力,从而使接口更为健壮. 限流 ...

  10. Robot Framework原生库的编辑与应用

    RF有一些操作指令不存在,需要自己添加方法,比如selenium里有click_and_hold指令(鼠标保持点击状态)而RF内没有.所以需要在库文件里加入这个方法 C:\Python27\Lib\s ...