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;
}
posted @ 2026-01-14 23:35  江蝶  阅读(12)  评论(0)    收藏  举报