Codeforce Round #762 Div3
今天vp了下这套div3,前三题倒很顺利,做d的时候看错题,mn读反了,并且搞了一个错误的维护次大和最大的方案,虽然这个方案好像也能做,但是确实很麻烦,然后看E也没顺利调出来,没有想清楚具体操作的过程,F,G赛后都补了
A.Square String?
问有没有相同的字符串,可以简单的考虑奇数不行,偶数就切一般就行
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t,n;
string s;
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>s;
ll len=s.size();
ll n=len/2;
int fl=0;
if(len%2)fl=1;
for(int i=0;i<n;i++)
if(s[i]!=s[i+n])fl=1;
if(!fl)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
B.Squares and Cubes
问你一个数以内有多少数可以是某个数的乘方或立方,搞个map把所有这种数放进去,然后把所有出现的节点记录一下,对每个查询我们遍历下就行,需要多处理一些,不然最后极限数据找不到下一个位置会出错
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t,n,cnt;
ll a[1000010];
map<ll,ll>mp;
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>t;
for(int i=1;i*i<=1e9;i++)
{
mp[i*i]=1;
}
for(int i=1;i*i*i<=1e9;i++)
mp[i*i*i]=1;
ll le=mp.size();
for(auto x:mp)
{
a[++cnt]=x.first;
}
while(t--)
{
cin>>n;
ll ans=0;
for(int i=1;i<=cnt;i++)
{
if(a[i]>n){
ans=i-1;
break;
}
}
if(ans==0)ans=32591;
cout<<ans<<endl;
}
return 0;
}
C.Wrong Addition
给你一个加法规则,按他的要求模拟下就行,没啥难度的小模拟
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t,a,s;
vector<ll>ans;
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>t;
while(t--)
{
ans.clear();
cin>>a>>s;
ll nowa=1,nows=1;
ll fl=0;
while(s)
{
if(s%10>=a%10)
{
ans.push_back(s%10-a%10);
s/=10;
a/=10;
}
else {
ll tmp=10+(s%10);
ans.push_back(tmp-a%10);
if((s/10)%10!=1){
fl=1;
break;
}
s/=100;
a/=10;
}
}
if(a||fl)cout<<"-1";
else
{
reverse(ans.begin(),ans.end());
ll le=ans.size();
ll fl=0;
for(int i=0;i<le;i++)
{
if(ans[i]!=0)fl=1;
if(fl)cout<<ans[i];
}
}
cout<<endl;
}
return 0;
}
D.New Year’s Problem
n个人m个商店,给你每个人收到每个商店礼物获得的快乐值,最多去n-1个商店请问最小的快乐值最大可以多大。
刚开始一个错误的思路,维护最大次大,后面发现有点反例。就寄了,没考虑这个问题是单调的可以二分解决。我们二分这个最小的快乐值,对每个人我们尝试看有没有一个商店同时能让两个人都收到的值大于x并且其他人的最大值都大于x,就说明二分可行,注意二分的边界问题,如果用r-1的时候,就(l+r+1)>>1 并且check里不能memset for循环赋值。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t,n,m,tmp[100010];
vector<ll>a[100010];
ll check(ll x)
{
ll fl=0;
for(int i=0; i<n; i++)
tmp[i] = 0;
for(int i=1;i<=m;i++)
{
ll cnt=0;
for(int j=0;j<n;j++)
{
if(a[i][j]>=x)
{
cnt++;
tmp[j]=a[i][j];
}
}
if(cnt>=2)fl=1;
}
for(int i=0;i<n;i++)
if(!tmp[i])fl=0;
return fl;
}
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>t;
while(t--)
{
ll x;
cin>>m>>n;
for(int i=1;i<=m;i++)
a[i].clear();
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
cin>>x;
a[i].push_back(x);
}
}
ll l=0,r=1e9;
while(l<r)
{
ll mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<l<<"\n";
}
return 0;
}
E - MEX and Increments
对0-n的每个数维护,如果让序列mex是他所需要的最小代价。排个序,如果一个数前面没有足够的比他小的数那他就不可能构造出来,如果一个数不可能出现,他后面所有都不可能出现,可以用桶纪录每个数出现次数,然后就把所有比较小的数存栈里,记录一个值表示让前面全部满足要求所需要的代价,因为栈后进的都是大的,所以我们优先使用后进去的,这个值代表,让0-i里所有数都出现过需要的最小代价,啥时候栈空了代表,不可能出现这种情况,就记录一下,后面都会寄
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t,n,a[200010],s[200010],cnt[200010],ss[200010];
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>n;
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)
cin>>a[i],cnt[a[i]]++;//桶排
stack<ll>stk;//因为按顺序,所以单调
ll fl=0;//记录是否还可行
ll sum=0;//记录前面所需贡献
for(int i=0;i<=n;i++)
{
if(fl==1)cout<<"-1"<<" ";
else cout<<cnt[i]+sum<<" ";//自己要全部+1,前面要到位
while(cnt[i]--)stk.push(i);//全部搞进去,因为这个值暂时可用
if(!stk.empty())//如果没东西用,mex不可能搞出来,之后都是-1
{
sum+=i-stk.top();//贡献尽量用刚进来的,因为栈其实是单调的
stk.pop();
}
else fl=1;
}
cout<<endl;
}
return 0;
}
F.Let’s Play the Hat?
感觉挺有趣的一个思维题,就是给n个人,k轮游戏,每轮游戏玩m次,分桌子可以分n/m和n/m向上取整两种桌子,想让所有人在大桌子玩的次数差距不超过2,其实分析下,每轮游戏,我们都可以分为n%m次大桌子和n-n%m次小桌子,然后每轮游戏我们记录下上轮游戏最后一次用的大桌子位子,下一轮游戏从这个开始就行。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t,n,m,k;
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>n>>m>>k;
ll now=0,p=0;
for(int i=1;i<=k;i++)
{
now=p;
for(int j=1;j<=m;j++)
{
ll tmp=n/m+(j<=n%m);//n个桌子,总有n%m个桌子需要大一号
cout<<tmp<<" ";
for(int k=1;k<=tmp;k++)
{
now=now%n+1;
cout<<now<<" ";
}
if(j<=n%m)p=now;//循环队列,维护第i论结束后,大桌最后一次出现的位置,从这里开始
//避免大桌重复在一些地方使用
cout<<"\n";
}
}
cout<<endl;
}
return 0;
}
G.Unusual Minesweeper
一个图,有很多雷的坐标和他们自动引爆的时间,没个雷可以覆盖的范围是他上下左右延申k格,一个雷爆了,他覆盖范围其他雷都一起爆。每秒我们可以自己提前让一个雷爆炸,其实思路挺好想,所有互相能覆盖的类用并查集维护成一种类型的雷,然后没个类型雷爆炸的时间是他们所有时间的最小值,按x轴y轴分别排序合并一次,然后就给所有组的雷引爆排个序,大的放前面,每秒可以引爆一个最大的,时候这个时刻不需要我引爆也能爆就结束,答案就是当前的时刻
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll _,n,k,fa[200010],t[200010],b[200010];
struct node{
ll x,y,id;
}a[200010];
bool cmp1 (const node x1,const node x2)
{
return x1.x==x2.x?x1.y<x2.y:x1.x<x2.x;
}
bool cmp2 (const node x1,const node x2)
{
return x1.y==x2.y?x1.x<x2.x:x1.y<x2.y;
}
ll find(ll x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void marge(ll a,ll b)
{
ll tmpa=find(a);
ll tmpb=find(b);
fa[tmpa]=tmpb;
t[tmpb]=min(t[tmpa],t[tmpb]);
}
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>_;
while(_--)
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y>>t[i];
fa[i]=i;
a[i].id=i;
}
sort(a+1,a+1+n,cmp1);//横着合并一次
for(int i=2;i<=n;i++)
{
if(a[i].x==a[i-1].x&&a[i].y-a[i-1].y<=k)
marge(a[i].id,a[i-1].id);
}
sort(a+1,a+1+n,cmp2);//竖着合并一次
for(int i=2;i<=n;i++)
{
if(a[i].y==a[i-1].y&&a[i].x-a[i-1].x<=k)
marge(a[i].id,a[i-1].id);
}
ll cnt=0;
for(int i=1;i<=n;i++)
{
if(fa[i]==i)
b[++cnt]=t[i];
}
sort(b+1,b+1+cnt,greater<ll>());//最大的肯定要早消掉
b[cnt+1]=0;
int i;
for(i=1;i<=cnt;i++)
if(b[i+1]<=i-1)break;
cout<<i-1<<"\n";
}
return 0;
}