Loading

Codeforces Round #772 (Div. 2)

A

大概是可以把两个数 \(x\)\(y\) 替换成 \(a\)\(b\),满足 \(x|y=a|b\),求最终的和最小。这东西直接贪心就好了,由于是或起来,最终序列内所有数的或和是不变的,那我们直接贪心构造,最终和一定是大于等于这个或和的,那我们把一个数变成或和,别的都是 \(0\),就是最小的和。

$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
void solve(){
    int n,x,ans=0;cin>>n;
    while(n--) cin>>x,ans|=x;
    cout<<ans<<'\n';
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;for(cin>>T;T--;)
        solve();
    return 0;
}

B

每次可以把一个数改成 \([1,10^9]\) 内的任意一个数,求最少多少次满足没有一个 \(i\) 使得 \(a_i>a_{i-1}\)\(a_i>a_{i+1}\)。发现是严格大于,所以不难发现,等于是一个非常好的情况。于是考虑从前往后做,这时候贪心希望能把后面不合法的破坏掉。于是我们对于一个不合法的位置 \(i\),把 \(i+1\) 的位置赋值为 \(i+1\) 位置相邻两个中的最大值,这样假如说后面有一个位置不合法就把它啊掉了并且不会产生新的不合法位置。

$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
ll a[MAXN];
void solve(){
    int n,ans=0;cin>>n;
    rep(i,1,n) cin>>a[i];
    rep(i,2,n-1){
        if(a[i]>a[i-1]&&a[i]>a[i+1]){
            if(i==n-1) a[i+1]=a[i];
            else a[i+1]=max(a[i],a[i+2]);
            ans++;
        }
    }
    cout<<ans<<'\n';
    rep(i,1,n) cout<<a[i]<<' ';
    cout<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;for(cin>>T;T--;)
        solve();
    return 0;
}

C

考虑倒数第三个位置,只能变成最后两个数相减的结果。最后两个数都不能变。

然后假如说最后一个数是负数,那么对于位置 \(i\),它后面都是递增的,只有 \(a_i>a_{i+1}\)。那这个位置不可能变得比 \(a_{i+1}\) 小。

然后假如说最后一个数不是负数,那只有最后两个数是递减的时候是不行的,别的情况我们把每个位置赋值为最后两个数相减,题目允许等于。

所以分类判断以下就可以了。

$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
ll a[MAXN];
void solve(){
    int n;cin>>n;
    rep(i,1,n) cin>>a[i];
    if(a[n]<0){
        bool fl=1;
        rep(i,2,n) if(a[i]<a[i-1]){fl=0;break;}
        if(fl) cout<<0<<'\n';
        else cout<<-1<<'\n';
    }else{
        if(a[n]<a[n-1]){cout<<-1<<'\n';return;}
        cout<<n-2<<'\n';
        rep(i,1,n-2) cout<<i<<' '<<n-1<<' '<<n<<'\n';
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;for(cin>>T;T--;)
        solve();
    return 0;
}

D

考虑一个简化的问题,如果保证了,\(a\) 中的每个数间都不能通过这些操作变成相同的,也就是每个数间是独立的,那怎么做?直接令 \(dp_i\) 表示二进制有 \(i\) 位的数的个数,然后先把 \(dp[\operatorname{log}_2a_i]\) 都加一,然后做斐波那契的递推就好了。

问题是怎么去重。我们把 \(a[]\) 排序,容易发现,如果 \(a_i<a_j\) 并且 \(a_i\) 能够变成 \(a_j\),那么 \(a_j\) 能够通过在后面删去得到 \(a_i\)。所以我们对于一个数 \(a_i\),我们把比它小的数去重,即所有能变成这个数的数都删掉。具体地,我们每次判断二进制最后一位和两位,是否满足操作加上后的结果,如果满足,那么把这个操作撤销然后把得到的这个数给删了。

$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
const int MOD=1e9+7;
int a[MAXN],dp[MAXN];
unordered_map<int,vector<int> > cnt;
void ins(int x,int id){
    while(x){
        cnt[x].pb(id);
        if(x&1) x>>=1;
        else if(!(x&2)) x>>=2;
        else break;
    }
}
void solve(){
    int n,p;cin>>n>>p;
    rep(i,1,n) cin>>a[i];
    sort(a+1,a+1+n);
    per(i,n,1){
        if((int)cnt[a[i]].size()>0)
            for(auto j:cnt[a[i]])
                a[j]=0;
        ins(a[i],i);
    }
    rep(i,1,n) if(a[i]) dp[__lg(a[i])+1]++;
    rep(i,2,p) dp[i]=(dp[i]+dp[i-1]+dp[i-2])%MOD;
    int ans=0;
    rep(i,1,p) ans=(ans+dp[i])%MOD;
    cout<<ans<<'\n';
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // int T;for(cin>>T;T--;)
        solve();
    return 0;
}

E

考虑对于每一对限制,两辆车的方向一定是不同的,所以对此我们可以黑白染色,然后对于每个连通块,就转化为给定若干组大小关系,求一组满足题意的解,这东西用拓扑跑一下就行了。

一个小坑点就是初始两辆车的坐标不能相同。(好像比 D 简单好多)

$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define NOed0 {cout<<"NO"<<'\n';return 0;}
#define NOed {cout<<"NO"<<'\n';return;}
#define NO cout<<"NO"<<'\n';
#define YESed0 {cout<<"YES"<<'\n';return 0;}
#define YESed {cout<<"YES"<<'\n';return;}
#define YES cout<<"YES"<<'\n';
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
vector<int> e1[MAXN],e2[MAXN],vlm[MAXN];
struct Lim{
    int typ,i,j;
    void input(int id){
        cin>>typ>>i>>j;vlm[i].pb(id),vlm[j].pb(id);
        e1[i].pb(j);e1[j].pb(i);
    }
}lm[MAXN];
//col=-1->L col=1->R
int col[MAXN],deg[MAXN],val[MAXN];
vector<int> cur;
bool bfs(int st){
    queue<int> q;q.push(st);
    col[st]=1;cur.clear();
    while(!q.empty()){
        int x=q.front();q.pop();cur.pb(x);
        for(int s:e1[x]){
            if(col[s]){
                if(col[s]==col[x]) return 0;
                continue;
            }col[s]=-col[x];q.push(s);
        }
    }return 1;
}
int cv=-1e9;
bool getval(){//edge from small to large
    for(int x:cur) deg[x]=0,e2[x].clear();
    for(int x:cur)
        for(auto j:vlm[x]){
            int u=lm[j].i,v=lm[j].j,t=lm[j].typ;
            if(t==1){
                if(col[u]==-1) e2[u].pb(v),deg[v]++;
                else e2[v].pb(u),deg[u]++;
            }else{
                if(col[u]==-1) e2[v].pb(u),deg[u]++;
                else e2[u].pb(v),deg[v]++;
            }
        }
    vector<int> topo;
    queue<int> q;
    for(int x:cur) if(!deg[x]) q.push(x);
    while(!q.empty()){
        int x=q.front();q.pop();
        topo.pb(x);
        for(int s:e2[x]){
            if(--deg[s]==0)
                q.push(s);
        }
    }
    for(int x:cur) if(deg[x]) return 0;
    for(int x:topo) val[x]=cv,cv++;
    return 1;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int n,m;cin>>n>>m;
    rep(i,1,m) lm[i].input(i);
    rep(i,1,n){
        if(col[i]) continue;
        if(!bfs(i)) NOed0;
        if(getval()) continue;
        for(int x:cur) col[x]=-col[x];
        if(!getval()) NOed0;
    } YES
    rep(i,1,n){
        if(col[i]==-1) cout<<'L'<<' ';
        else cout<<'R'<<' ';
        cout<<val[i]<<'\n';
    }
    return 0;
}

F

牛逼题。看了官方题解。

考虑对整个序列怎么做。令 \(L_i\) 表示在 \(i\) 左边的第一个权值小于等于 \(w_i\) 的点,\(R_i\) 表示在 \(i\) 右边第一个权值小于等于 \(w_i\) 的点。然后结论就是,最终的最小的对必然在所有的 \((L_i,i)\)\((i,R_i)\) 中产生。

证明:考虑对于一个最小的对 \((a,b)\) 满足 \(a<b\),那么如果有 \(w_a\le w_b\),那么 \(a\) 只能是 \(L_b\),否则会有更好的对 \((a,L_b)\)。同理如果 \(w_a>w_b\),那么 \(b\) 只能是 \(R_a\),否则会有更优解 \((R_a,b)\)。证毕。

而这个结论在某个区间中同样适用。

posted @ 2022-02-21 13:21  ZCETHAN  阅读(71)  评论(0编辑  收藏  举报