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递减的过程中逐步进行字段的合并即可,对于这个合并过程,可以用并查集来实现
#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;
}
F.Cherry Tree
给定一棵无向有根树,根为1,每个叶子上面都有一颗樱桃,如果对某个点进行摇动,会把它(叶子无子树)及其子树上的樱桃全部摇下来,但是如果被摇超过两次就不合法。问是否可以在合法的前提下用3的倍数的摇动次数把所有樱桃摇下来
DP思路:维护一个bool型DP数组dp[i][k],表示这个点是否能通过摇动k次得到子树上的cherry, 因为只考虑3倍数,直接对3取模,然后向上进行DP即可,DP[i][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;
vi G[N+5];
vector<bool>dp[N+5];
void merge(vector<bool>& bef,vector<bool>& fur,vector<bool>& res){
for(int i=0;i<3;i++){
if(!bef[i]) continue;
for(int j=0;j<3;j++){
if(!fur[j]) continue;
res[(i+j)%3]=1;
}
}
}
void dfs(int u,int fa){
vi sons;
for(auto v:G[u]){
if(v!=fa){
dfs(v,u);
sons.pb(v);
}
}
dp[u].resize(3);
if(sons.size()){dp[u][0]=1;
for(auto v:sons){
vector<bool>tmp(3,0);
merge(dp[u],dp[v],tmp);
dp[u]=tmp;
}
}
dp[u][1]=1;
}
void solve(){
int n;
cin>>n;
for(int i=0;i<=n;i++) G[i].clear(),dp[i].clear();
for(int i=0;i<n-1;i++){
int u,v;
cin>>u>>v;
G[u].pb(v);
G[v].pb(u);
}
dfs(1,-1);
cout<<(dp[1][0]?"Yes\n":"No\n");
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}
G.Nastiness of Segments
题意:对于数组的一段子数组[l,r][a[l],a[r]],如果在[0,r-l]间存在一个d,使得min(a[l],a[l+1],...,a[l+d])=d,那么就称这个d对于区间[l,r]是恶心的,在给定的数组上面进行单点修改和区间查询,查询的内容是[l,r]上有多少各恶心的d
min是非增的,而d是严格单调增的,所以只会有一个解,固定l时,f(d)=d,g(d)=min(a[l],...,a[l+d]),那么就是先定位到解的具体区间(第一个f(d)-g(d)>=0的d作为右端点,l作为左端点),接着在区间里面找到a[]的最小值,使得正好等于d。
用线段树RMQ实现
貌似一般不建议把函数命名为find
#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+5,MOD=1e9+7;
int a[N],t[N<<2],curmin,ans;
void build(int o,int l,int r){
if(l==r){t[o]=a[l];return ;}
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
t[o]=min(t[o<<1],t[o<<1|1]);
}
void upd(int o,int l,int r,int pos,int val){
if(pos>r||pos<l) return ;
if(l==r){
t[o]=val;
a[pos]=val;
return ;
}
int mid=(l+r)>>1;
upd(o<<1,l,mid,pos,val);
upd(o<<1|1,mid+1,r,pos,val);
t[o]=min(t[o<<1],t[o<<1|1]);
}
int qrymin(int o,int l,int r,int lq,int rq){
if(rq<l||lq>r) return INF;
if(l>=lq&&r<=rq) return t[o];
int mid=(l+r)>>1;
return min(qrymin(o<<1,l,mid,lq,rq),qrymin(o<<1|1,mid+1,r,lq,rq));
}
void find(int o,int l,int r,int lq,int rq){
if(rq<l||lq>r||ans!=INF) return ;
if(lq<=l&&r<=rq&&min(curmin,t[o])>r-lq){
curmin=min(curmin,t[o]);
return ;
}
if(l==r){ans=l;return;}
int mid=(l+r)>>1;
find(o<<1,l,mid,lq,rq);
find(o<<1|1,mid+1,r,lq,rq);
}
void solve(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
while(q--){
int op;
cin>>op;
if(op==1){
int pos,val;
cin>>pos>>val;
upd(1,1,n,pos,val);
}else{
int l,r;
cin>>l>>r;
if(qrymin(1,1,n,l,r)>r-l){
cout<<"0\n";
continue;
}
ans=curmin=INF;
find(1,1,n,l,r);
if(ans!=INF&&qrymin(1,1,n,l,ans)==ans-l) cout<<"1\n";
else cout<<"0\n";
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}

浙公网安备 33010602011771号