模拟赛
炸裂场,交互 + 提答
T1 ラーメンの食べ比べ
交互题,没做过。。。
\(N \le 400\),有 \(600\) 次询问,其实还挺水的。
先考虑二分,两两一组比较,会得到 \(200\) 个较大的和 \(200\) 个较小的,还剩 \(400\) 次查询。
既然还剩 \(400\) 次查询,那也不用考虑二分了,直接 \(200\) 个暴力比较就好了。
注意交互写法。
code
#include<bits/stdc++.h>
using namespace std;
#include "ramen.h"
const int N = 605;
int cnt1,cnt2;
int a[N],b[N];
void Ramen(int N)
{
if(N==1)
{
Answer(0,0);
}
for(int i=0;i<N;i+=2)
{
if(i+1>=N) break;
if(Compare(i,i+1)==1) a[++cnt1]=i,b[++cnt2]=i+1;
else a[++cnt1]=i+1,b[++cnt2]=i;
}
if(N&1)
{
if(Compare(N-1,a[1])==1) a[1]=N-1;
else b[++cnt2]=N-1;
}
int mx=a[1];
for(int i=2;i<=cnt1;i++)
{
if(Compare(mx,a[i])==-1) mx=a[i];
}
int mi=b[1];
for(int i=2;i<=cnt2;i++)
{
if(Compare(b[i],mi)==-1) mi=b[i];
}
Answer(mi,mx);
}
T2 Substring of Sorted String
其实挺裸的线段树。
我们考虑一个区间如果是合法的,那么它一定是单调不下降的。
如果在线段树中直接维护是否合法,还需要分讨合并。所以我们不如只维护区间的单调性,先不管是否合法。
于是维护的信息有左右端点,各个字母出现次数(最后要用),单调性。
最终我们对查询的区间中字母的出现次数和整个序列的出现次数比较。
除了边界的两个以外,其他字母的出现次数都应该和整个序列的出现次数一样。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n,m,cnt[26];
char s[N],t[N];
struct T
{
int l,r,cnt[26];
int mx,mi;
bool y=1;
} tr[N<<2];
void pushup(int k)
{
tr[k].mi=tr[k<<1].mi; tr[k].mx=tr[k<<1|1].mx;
for(int i=0;i<26;i++) tr[k].cnt[i]=tr[k<<1].cnt[i]+tr[k<<1|1].cnt[i];
if(!tr[k<<1].y||!tr[k<<1|1].y) tr[k].y=0;
else tr[k].y=tr[k<<1].mx<=tr[k<<1|1].mi;
}
void bui(int k,int l,int r)
{
tr[k].l=l; tr[k].r=r;
if(l==r) return tr[k].y=1,tr[k].mi=tr[k].mx=s[l]-'a',tr[k].cnt[s[l]-'a']++,void(0);
int mid=l+r>>1;
bui(k<<1,l,mid); bui(k<<1|1,mid+1,r);
pushup(k);
}
void mdf(int k,int p,char v)
{
if(tr[k].l==tr[k].r)
{
tr[k].cnt[s[p]-'a']--;
tr[k].cnt[v-'a']++;
cnt[v-'a']++; cnt[s[p]-'a']--;
tr[k].mx=tr[k].mi=v-'a';
s[p]=v; tr[k].y=1;
return;
}
int mid=tr[k].l+tr[k].r>>1;
if(p<=mid) mdf(k<<1,p,v);
else mdf(k<<1|1,p,v);
pushup(k);
}
T que(int k,int l,int r)
{
if(l<=tr[k].l&&tr[k].r<=r)
{
return tr[k];
}
int mid=tr[k].l+tr[k].r>>1;
if(r<=mid) return que(k<<1,l,r);
else if(l>mid) return que(k<<1|1,l,r);
else
{
T r1=que(k<<1,l,r),r2=que(k<<1|1,l,r),r3;
r3.mi=r1.mi; r3.mx=r2.mx;
for(int i=0;i<26;i++) r3.cnt[i]=r1.cnt[i]+r2.cnt[i];
if(!r1.y||!r2.y) r3.y=0;
else r3.y=r1.mx<=r2.mi;
return r3;
}
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("o2.out","w",stdout);
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++) cnt[s[i]-'a']++;
bui(1,1,n);
scanf("%d",&m);
while(m--)
{
int c; scanf("%d",&c);
if(c==1)
{
int x;char y; scanf("%d %c",&x,&y);
mdf(1,x,y);
}
else
{
int l,r; scanf("%d%d",&l,&r);
T q=que(1,l,r);
if(q.y)
{
bool fl=0;
for(int i=q.mi+1;i<q.mx;i++) if(q.cnt[i]!=cnt[i])
{
fl=1; break;
}
if(fl) puts("No");
else puts("Yes");
}
else puts("No");
}
}
return 0;
}
T3 Edge Elimination
一开始觉得贪心挺好想的,每次找出大于 \(x\) 的最小的子树,然后从大到小删子树。
然后大样例挂了一个。。。
其实再想一想会发现上述情况不一定是最优的,考虑最终删成一条链的情况,这个贪心显然就假了。
那我们就遍历一下最上端留下的节点是哪个,也就是遍历留下的树的根节点的深度。
其实到这里就已经 AC 了。但是还有一种奇葩思路。(以下思路正确性不保证)
其实遍历深度是不必要的,因为如果一棵大于 \(x\) 的非最小的子树能成为答案的话,
那么一定能通过整棵树删去子树达到,所以只需要判断整棵树和大于 \(x\) 的最小的子树就可以。(至少可以过)
code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
int t;
LL k,d,x;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld%lld",&d,&k,&x);
LL tot=0,left; LL ans=0,ans1=0;
for(int i=0;i<=d;i++)
{
tot=tot*k+1;
if(tot==x)
{
if(i==d) printf("0\n");
else printf("1\n");
goto X;
}
else if(tot>x)
{
ans=(i==d)?0:1,left=tot;
while(left>x)
{
tot=(tot-1)/k;
LL tmp=(left-x)/tot;
left-=tmp*tot; ans+=tmp;
}
break;
}
}
tot=0;
for(int i=0;i<=d;i++) tot=tot*k+1; left=tot;
while(left>x)
{
tot=(tot-1)/k;
LL tmp=(left-x)/tot;
left-=tmp*tot; ans1+=tmp;
}
printf("%lld\n",min(ans1,ans));
X:;
}
return 0;
}
T4 双人猜数游戏
提答,挺好玩的,先咕着。。。
咕咕咕。。。
标签:tmp,27,2024.7,int,LL,tot,++,模拟,left From: https://www.cnblogs.com/ppllxx-9G/p/18345735