简直了.一上来A题,我说这题很水的,我用二分搞一下求稳.结果二分写挂了.然后我暴力枚举并判断,又挂了.最后我不得不 O(1) O ( 1 ) 出答案,一分钟标算就出来了.我都做了什么……
B题尺取法一搞就稳了.然后C题我差点就骂人了.D题我觉得自己能写出来,然后推了半天,放弃了.我又回去搞C题,终于在离比赛结束还有9分钟的时候非常危险地A掉了.
好的. O(1) O ( 1 ) 出答案.怎么出呢?
#include
using namespace std;
typedef long long ll;
int lp,rp,a;
int main()
{
cin>>lp>>rp>>a;
cout<2,(lp+a)),(rp+a))*2;//全部人数/2,左手人数,右手人数的最小值.游戏结束.
}
顺利起来了.维护A数组的前缀和sum1,B数组的前缀和sum2,当sum1=sum2的时候更新一下ans即可.
#include
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 (;sum1for (;sum1>sum2&&l<=n&&r<=m;) sum2+=b[r++];
}
cout<1;
}
我给你们讲讲故事.
首先我写出来的代码是这样的.
#include
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 (resreturn 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
#define sit set::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 (resreturn 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).
亏我能写出来.
我离推出标算成功就差了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
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);
}
}
后面两个题一看就非常诡异,我放弃了.