Bootstrap

Codeforces Round #469 div.2 A-D 思路及题解

其实这场我差点就掉分了

简直了.一上来A题,我说这题很水的,我用二分搞一下求稳.结果二分写挂了.然后我暴力枚举并判断,又挂了.最后我不得不 O(1) O ( 1 ) 出答案,一分钟标算就出来了.我都做了什么……
B题尺取法一搞就稳了.然后C题我差点就骂人了.D题我觉得自己能写出来,然后推了半天,放弃了.我又回去搞C题,终于在离比赛结束还有9分钟的时候非常危险地A掉了.

A

好的. O(1) O ( 1 ) 出答案.怎么出呢?

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int lp,rp,a;

int main()
{
cin>>lp>>rp>>a;
cout<<min(min((lp+rp+a)/2,(lp+a)),(rp+a))*2;//全部人数/2,左手人数,右手人数的最小值.游戏结束.
}

B

顺利起来了.维护A数组的前缀和sum1,B数组的前缀和sum2,当sum1=sum2的时候更新一下ans即可.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,a[123456],b[123456];
int main()
{
int i,l=2,r=2;
cin>>n>>m;
for (i=1;i<=n;++i) scanf("%lld",&a[i]);
for (i=1;i<=m;++i) scanf("%lld",&b[i]);
ll sum1=a[1],sum2=b[1],ans=0;
for (;l<=n&&r<=m;)
  {
  if (sum1==sum2) ans++,sum1+=a[l++];
  for (;sum1<sum2&&l<=n&&r<=m;) sum1+=a[l++];
  for (;sum1>sum2&&l<=n&&r<=m;) sum2+=b[r++];
  }
cout<<ans+1;
}

C

我给你们讲讲故事.
首先我写出来的代码是这样的.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char c[205080];int vis[204045];//用vis数组统计每一个字符是不是已经用过了
vector<int> zeb[200874];
int main()
{
scanf("%s",c+1);
int n=strlen(c+1),ans=0,i,j;
for (i=1;i<=n;++i) if (!vis[i]&&c[i]!='1')//是0且没有被用过
  {
  zeb[++ans].push_back(i),vis[i]=1;//新建一个zebra串
  char last='0';//last标记该串的上一个字符
  for (j=i+1;j<=n;++j) 
    if (c[j]!=last&&!vis[j]) 
      last=c[j],vis[j]=1,zeb[ans].push_back(j);//0,1,0,1搜下去,直到结束.
  if (last!='0') return puts("-1"),0;//last不是0,显然不可能,直接输出-1.
  }
int res=0;
for (i=1;i<=ans;++i) res+=zeb[i].size();
if (res<n) return puts("-1"),0;//再加一条,所有答案的和必须为n
printf("%d\n",ans);//输出.
for (i=1;i<=ans;++i,puts("")) 
  {
  printf("%d ",zeb[i].size());
  for (int j:zeb[i]) printf("%d ",j);
  }
}

考虑数据为200000个’0’.显然tle了.
然后我花了1个小时30分钟研究了如何优化这个算法.
某大佬说用队列并查集搞一搞.我一听就觉得这方法不适合我.
我又仔细想了想,越想越不对.
我突然想到了什么.所谓优化,就是把对于某个0查找下一个1的位置和对于某个1查找下一个0的位置的效率增高.我们显然需要一个stl进行优化.用什么?set!
使用set进行瞎搞,之后AC代码如下.

#pragma GCC optimize("inline,Ofast",3)
#include<bits/stdc++.h>
#define sit set<int>::iterator 
using namespace std;
typedef long long ll;
set<int> zero,one;//考虑用两个set存储0和1的位置
char c[252555];
vector<int> zeb[205085];
int main()
{
scanf("%s",c+1);
int n=strlen(c+1),i,j;
for (i=1;i<=n;i++) c[i]=='0'?zero.insert(i):one.insert(i);//插入
int ans=0;
for (;!zero.empty();)//瞎搞,我都不知道自己怎么写的了.
  {
  sit p;int tmp=*zero.begin();
  zeb[++ans].push_back(tmp);//找到最前面的一个
  zero.erase(zero.begin());//删掉
  for (;one.upper_bound(tmp)!=one.end()&&!zero.empty();)
    {
    p=one.upper_bound(tmp);//在另一个set里查找比这个大的
    tmp=*p;
    zeb[ans].push_back(tmp);
    one.erase(p);
    p=zero.upper_bound(tmp);//再返回去查找
    tmp=*p;
    if (p==zero.end()) return puts("-1"),0;
    zeb[ans].push_back(tmp);
    zero.erase(p);
    }
  }
int res=0;//后面一样的判断,一样的输出.
for (i=1;i<=ans;++i) res+=zeb[i].size();
if (res<n) return puts("-1"),0;
printf("%d\n",ans);
for (i=1;i<=ans;++i,puts("")) 
  {
  printf("%d ",zeb[i].size());
  for (int j:zeb[i]) printf("%d ",j);
  }
}//显然就是O(nlogn).

亏我能写出来.

D

我离推出标算成功就差了1%,太遗憾了.

/*
首先可以看到所有$1-(n+1>>1)$之间的数字都没有变化.然后我们手膜一下n=13时候的跳法.
可以发现,对于n来说,它先往回1,然后跳了2,接下来4,8,16......
对于n-1来说,先是3,然后6,12,24......
n-2,5,10,20,40......好了.
也就是说,对于每一个位置大于n的数x,它肯定向左跳某个奇数长度,然后加倍,再加倍......直到x位置小于或者等于n.
所以只要模拟询问,向回跳回去就可以了.
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x,n;int q;
int main()
{
scanf("%lld%d",&n,&q);
for (;q--;)
  {
  scanf("%lld",&x);
  for (;x%2==0;x+=n-x/2);//当x是偶数的时候,它加上n-x/2.这一步比较骚.可以思考一下.
  printf("%lld\n",x+1>>1);
  }
}

后面两个题一看就非常诡异,我放弃了.

;