赛时还剩一个小时开D题,虽然第一眼就看出来怎么写了
(可能因为最近在练状压dp),但是因为题目理解错了,还以为自己想错了,就写了个假的算法,到最后判然醒悟的时候已经就剩20分钟了,最后神志不清交了D四次罚时。等比赛结束冷静之后发现是下标有问题(经典状压从0还是从1开始写错了)
最后赛后测评结束后再提交就通过了。总之很遗憾吧,但感觉自己确实进步了不少,一开始D都是写不出来的,后来第一次赛时all in出了D,到现在能差点时间就能赛时4题。
A. Pizza Time
初见端倪,发现出题人喜欢分组是吧
。
code
#include <bits/stdc++.h>
#define intt long long
#define N
#define debug(x) cout<<#x<<":"<<x<<" ";
using namespace std;
//using namespace __gnu_pbds;
int T,n,m;
void solve()
{
cin>>n;
if(n<=2) cout<<0;
else
{
if((n-2)%2) cout<<(n-2)/2+1;
else cout<<(n-2)/2;
}
cout<<endl;
}
int main()
{
cin>>T;
while(T--)
{
solve();
}
return 0;
}
B. Strange Machine
对于没有除2的操作来说,我们需要特殊处理,以免跑到
O
(
n
)
O(n)
O(n)
对于有除2的操作来说,我们可以把减法和预处理,然后暴力模拟,最多不超过
O
(
l
o
g
n
)
O(log_n)
O(logn)
code
#include <bits/stdc++.h>
#define int long long
#define N 30
#define debug(x) cout<<#x<<":"<<x<<" ";
using namespace std;
//using namespace __gnu_pbds;
int T,n,m,a[N];
string s;
void solve()
{
cin>>n>>m;
cin>>s;
vector<int> v;
bool spj=0;
int sum=0;
for(int t=0;t<s.size();++t)
{
if(s[t]=='A')
{
sum+=1;
}
else
{
if(sum) v.push_back(sum);
sum=0;
v.push_back(0);
spj=1;
}
}
if(sum) v.push_back(sum);
for(int t=1;t<=m;++t)
{
int u1,ans=0;
cin>>u1;
if(spj)
{
int now=0,pos=0;
while(u1)
{
if(!v[pos])
{
u1/=2;
++now;
}
else
{
if(u1<=v[pos])
{
now+=u1;
u1=0;
}
else
{
now+=v[pos];
u1-=v[pos];
}
}
if(pos==v.size()-1) pos=0;
else pos++;
// debug(pos)
// debug(now)
// debug(u1)
// cout<<endl;
}
// cout<<"answer";
cout<<now<<endl;
}
else
{
// cout<<"answer";
cout<<u1<<endl;
}
}
}
signed main()
{
cin>>T;
while(T--)
{
solve();
}
return 0;
}
C. Maximum GCD on Whiteboard
分组构造题,和A熟悉的配方。
假设我们已经确定了最大公约数,我们只需要快速找出有多少个序列中的元素可以通过题目的方法变的合法。只要合法的个数满足题目要求就行。
对于一个数字我们可以发现一个结论,假设当前确定的最大公约数为k,那么只有当它为k,2k,3k,或者>=4k才是合法的元素(即本来就合法,或是可以通过题目的分组变得合法)。
所以我们从大到小枚举最大公约数,然后用树状数组或线段树维护>=4k的元素即可。
时间复杂度
O
(
n
l
o
g
n
)
O(nlog_n)
O(nlogn)
code
#include <bits/stdc++.h>
#define intt long long
#define N 200005
#define debug(x) cout<<#x<<":"<<x<<" ";
using namespace std;
//using namespace __gnu_pbds;
int T,n,m;
int tr[N],num[N];
void yadd(int u)
{
for(;u<=n;u+=u&(-u))
{
tr[u]++;
}
}
int yask(int u)
{
int sum=0;
for(;u;u-=u&(-u))
{
sum+=tr[u];
}
return sum;
}
void solve()
{
cin>>n>>m;
for(int t=0;t<=n;++t) tr[t]=0,num[t]=0;
for(int t=1;t<=n;++t)
{
int u1;
cin>>u1;
yadd(u1);
++num[u1];
}
for(int t=n;t>=1;--t)
{
int tot=num[t];
if(t*2<=n) tot+=num[t*2];
if(t*3<=n) tot+=num[t*3];
if(t*4<=n) tot+=yask(n)-yask(4*t-1);
if(tot+m>=n)
{
cout<<t;
cout<<endl;
break;
}
}
}
int main()
{
cin>>T;
while(T--)
{
solve();
}
return 0;
}
D. Find the Last Number
首先,我们可以先想最暴力的解法,那肯定是枚举1~n每一个数字,然后用它对n-1个数询问即可。
但是这样子询问次数肯定会超时。
我们发现,上述暴力的思路是每次可以排除一个数字,最后排除了n-1个数字,最后就剩下唯一确定的数字,那么我们可以借鉴上边的思路,比如和1进行&运算,可以发现我们直接排除了一半的答案(其实这一步也可以理解为判断是偶数和奇数),这样子我们就能优化暴力思路(一次枚举排除了一半的答案),以此类推,我们发现对剩下的数字采用相同的策略也同样可以筛选掉一半的元素,这样子我们通过每次一半一半的筛掉,就能确定最后的元素。
询问的总次数是
s
u
m
=
n
+
n
/
2
+
n
/
4
+
n
/
8
+
.
.
.
+
n
/
2
y
<
=
2
∗
n
sum=n+n/2+n/4+n/8+...+n/2^y<=2*n
sum=n+n/2+n/4+n/8+...+n/2y<=2∗n
code
#include <bits/stdc++.h>
#define intt long long
#define N 200005
#define debug(x) cout<<#x<<":"<<x<<" ";
using namespace std;
//using namespace __gnu_pbds;
int T,n,m;
int num[35][2],tot[35][2];
bool valid[N],validpos[N];
vector<int> v1,v2;
void solve()
{
cin>>n;
int ans=0;
for(int t=1;t<=n;++t) valid[t]=0,validpos[t]=0;
for(int t=0;t<=32;++t)
{
num[t][0]=num[t][1]=tot[t][0]=tot[t][1]=0;
}
int len=0;
for(int t=0;(1<<t)<=n;++t)
{
++len;
}
for(int t=1;t<=n;++t)
{
for(int i=0;i<len;++i)
{
if((t>>i)&1)
{
++num[i+1][1];
}
else ++num[i+1][0];
}
}
int u1=1,pos=1;
while(pos<=len)
{
bool spj;
v1.clear();
v2.clear();
for(int t=1;t<=n;++t)
{
if(valid[t])
{
if(t&u1)
{
++tot[pos][1];
}
else ++tot[pos][0];
}
}
for(int t=1;t<=n-1;++t)
{
if(validpos[t]) continue;
else
{
cout<<"? "<<t<<" "<<u1<<endl;
cin>>spj;
if(spj)
{
v1.push_back(t);
++tot[pos][1];
}
else
{
v2.push_back(t);
++tot[pos][0];
}
}
}
if(num[pos][1]==tot[pos][1])
{
for(int t=1;t<=n;++t)
{
if(valid[t]) continue;
if(t&u1)
{
valid[t]=1;
}
}
for(auto i:v1)
{
validpos[i]=1;
}
}
else
{
ans|=u1;
for(int t=1;t<=n;++t)
{
if(valid[t]) continue;
if(t&u1) continue;
else
{
valid[t]=1;
}
}
for(auto i:v2)
{
validpos[i]=1;
}
}
u1<<=1;
++pos;
}
cout<<"! "<<ans<<endl;
}
int main()
{
cin>>T;
while(T--)
{
solve();
}
return 0;
}
1671

被折叠的 条评论
为什么被折叠?



