1072 Div3练习
1072 Div3练习
今天是1.14,1.4且114
想回一下手感,但是。。。把CP editor装好了,但是感觉还是VScode更方便,不过在任何设备上都能敲上代码就足够了
A题不说了
B.Hourglass
这个题目思想很朴素,一开始题目没看明白想得太多就容易绕进去。
反正沙漏分两种情况,一个是(完全流完+空闲)周期性进行工作,这样直接计算即可。另一个是没有流完就翻过来了,但是这样仅仅只是把前一阶段流出来的 完完整整地流回去。所以只要抓住这两个阶段分析即可
新单词Hourglass
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define vi vector<int>
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int INF=1e18,N=2e5,MOD=998244353;
void solve(){
int n,k;
cin>>n>>k;
int l,r;
l=r=n;int tm=0;
while(r!=1){
if(l<=k&&k<=r) break;
tm++;
l>>=1;
r=(r>>1)+(r%2);
}
if(l<=k&&k<=r) cout<<tm<<endl;
else cout<<-1<<endl;
return ;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}
C.Huge Pile
这个题目一开始在想怎么判断10101里面有无100,但是实际上不用这样,二叉下来后直接分析是否落入区间即可,左端是>>1右端是>>1+%1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define vi vector<int>
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int INF=1e18,N=2e5,MOD=998244353;
void solve(){
int n,k;
cin>>n>>k;
int l,r;
l=r=n;int tm=0;
while(r!=1){
if(l<=k&&k<=r) break;
tm++;
l>>=1;
r=(r>>1)+(r%2);
}
if(l<=k&&k<=r) cout<<tm<<endl;
else cout<<-1<<endl;
return ;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}
D.Unfair Game
这个题目题面就是一坨,目前想到的是alice的最优策略:初始时知道奇偶性,奇则-1,偶则/2,没有赢之前返回的总是x,x是尾0的个数,所以就进行x次/2,做完之后再-1把尾1消掉,对返回的x同上操作。
一开始已知n是一个2^d,所以我们只需要找到d的数值是什么即可,一个while循环就好了,显然对于一个二进制数,Alice要想把它变成0,除了最高位的1只需要最后的一次操作之外,其它位上的0需要一次>>1,1需要一次-1和一次>>1所以当这些次数加起来超过k时,就是可以选择的数,具体的1的位置数目可以使用组合数公式计算.
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define vi vector<int>
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int INF=1e18,N=2e5,MOD=1e9+7;
int C[35][35];
void init(){
for(int i=0;i<=30;i++){
for(int j=0;j<=30;j++){
if(i<j) C[i][j]=0;
else if(j==0) C[i][j]=1;
else C[i][j]=C[i-1][j-1]+C[i-1][j];
}
}
}
void solve(){
int n,k;
cin>>n>>k;
int bits=0;
while(n%2==0){
n>>=1;
bits++;
}
int ans=0;if(bits+1>k) ans++;
for(int i=0;i<bits;i++)
for(int j=1;j<=i+1;j++)
if(i+j>k) ans+=C[i][j-1];
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
init();
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}
E.Exquisite Array
题目的意思是有一个长度为n的排列,一个k阶(单词不认识,暂且命名为阶好了)的数组是指任意两个相邻的数差的绝对值大于或等于k。然后我们要算出k从1到n-1的所有k阶数组(原数组的子数组)的个数。
当k为1时答案都是n(n-1)/2,也就是说对于我们知道了满足k的数组长度len时,它的任意子数组也满足k阶,数目一共为len(len-1)/2.而满足k+1阶的数组也一定满足k阶,所以我们就可以把原排列进行分段,一开始记录满足相邻差绝对值大于等于k的位置,然后在k递减的过程中逐步进行字段的合并即可,对于这个合并过程,可以用并查集来实现
//That's me.
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define vi vector<int>
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int INF=1e18,N=2e5,MOD=998244353;
vi sz,f;
int find(int x){
if(f[x]==x) return x;
f[x]=find(f[x]);
return f[x];
}
void merge(int a,int b){
int fa=find(a),fb=find(b);
if(fa>fb) swap(fa,fb);
sz[fa]+=sz[fb];
f[fb]=fa;
}
int cal(int x){
x=find(x);
return sz[x]*(sz[x]-1)/2;
}
void solve(){
int n;
cin>>n;
map<int,vi>mp;
int pre=0;
for(int i=0;i<n;i++){
int t;
cin>>t;
if(i) mp[abs(t-pre)].pb(i);
pre=t;
}
sz.resize(n);
f.resize(n);
for(int i=0;i<n;i++){f[i]=i;sz[i]=1;}
vi ans;
int cur=0;
for(int i=n-1;i>0;i--){
for(auto y:mp[i]){
cur-=cal(y);
cur-=cal(y-1);
merge(y,y-1);
cur+=cal(y);
}
ans.pb(cur);
}
reverse(ans.begin(),ans.end());
for(int i=0;i<n-1;i++) cout<<ans[i]<<' ';
cout<<endl;
sz.clear();f.clear();
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}

浙公网安备 33010602011771号