CF772

A

题意:

给一个数组,每一次操作如下:

选两个位置上的数字\(a_i\)\(a_j\),把他们变成\(x\)\(y\),要求满足\(a_i|a_j=x|y\)

进行任意多次操作后,整个数组的和最小是多少?

解答:

如果数组中的某个数字,在二进制下第\(k\)位是\(1\),那么可以利用有限多次上述操作,让整个数组中只有该数字,二进制下这一位是\(1\),但是不能消掉。

所以答案是数组中所有数字,二进制下出现过的位数的和,具体来说对所有数字取或就可以。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,sum;
    int a[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n;sum=0;
            for(int i=1;i<=n;++i)
            {
                cin>>a[i];
                sum|=a[i];
            }
            cout<<sum<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;   
}
/*

*/

B

题意:

给一个数组,每一次操作如下:

把一个位置上的数字改成\(1\)\(10^9\)中的任意一个数字

请用最少的修改次数,使得数组中没有\(a_i>a_{i-1}\)\(a_i>a_{i+1}\)的情况存在

解答:

从前往后考虑这个数组:

如果在某一个位置出现了\(a_i>a_{i-1}\)并且\(a_i>a_{i+1}\)的情况,而且前面都已经没有这种情况了。那么在这种情况下,显然修改\(a_{i+1}\)是最好的办法。

因为改前两个数字只能消掉当前这个小凸起,而改后面的数字有可能把后面凸起也消掉。

\(a_{i+1}\)不能改的太大,防止它本身又变成一个凸起,所以最好的方法是把\(a_{i+1}\)改成\(max\{a_i,a_{i+2}\}\)这样它消掉了当前凸起,本身不会成为凸起,后一个凸起也有可能因此消掉。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,sum;
    int a[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n;sum=0;
            for(int i=1;i<=n;++i) cin>>a[i];
            for(int i=2;i<n;++i)
            {
                if(a[i]>a[i-1]&&a[i]>a[i+1]) a[i+1]=max(a[i],a[i+2]),++sum;
            }
            cout<<sum<<'\n';
            for(int i=1;i<=n;++i) cout<<a[i]<<" \n"[i==n];
        }
    }
}
signed main()
{
    red::main();
    return 0;   
}
/*

*/

C

题意:

给一个数组,每次操作如下:

选择三个位置\(x<y<z\),令\(a_x=a_y-a_z\)

进行不超过\(n\)次操作,让数组不下降

解答:

只要构造一个合法解就可以了。

那么从后往前考虑这个数组,假设当前位置是\(x\),那显然\(x\)越小越好,不用管前面怎么样,因为\(x\)能变多小,前面就能变多小,一定不下降。

\(x\)最小的情况就是每个位置上的\(a_y\)去减后面最大的数字,这个可以通过倒序遍历数组的时候一起得到,实现可以看代码。

如果\(x\)变得最小还是不如前面小,那就寄了。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,sum;
    int a[N],maxn,minn,tmp;
    int pos1,pos2;
    bool flag;
    struct node
    {
        int x,y,z;
    }st[N];
    int top;
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n;sum=0;flag=1;top=0;pos1=pos2=n;
            for(int i=1;i<=n;++i) cin>>a[i];
            maxn=minn=a[n];tmp=1e9+7;
            for(int i=n-1;i;--i)
            {
                if(a[i]>a[i+1])
                {
                    if(tmp>a[i+1])
                    {
                        flag=0;break;
                    }
                    a[i]=tmp;
                    st[++top]=(node){i,pos2,pos1};
                    ++sum;
                }
                if(a[i]-maxn<tmp) tmp=a[i]-maxn,pos2=i;
                if(a[i]>maxn) maxn=a[i],pos1=i;
            }
            if(!flag){cout<<"-1\n";continue;}
            else
            {
                cout<<sum<<'\n';
                for(int i=1;i<=top;++i) cout<<st[i].x<<' '<<st[i].y<<' '<<st[i].z<<'\n';
            }
        }
    }
}
signed main()
{
    red::main();
    return 0;   
}
/*
1
5
-2 -1 -5 4 5

*/

D

题意:

给定一些初始整数,它们在一个集合里面。

这个集合规定,如果\(x\)在集合里,那么\(2*x+1\)\(4*x\)也在集合里。

请问有多少小于\(2^p\)的数字在这个集合里面?

解答:

首先,如果\(x\)在集合里,那么\(2*x+1\)\(4*x\)也在集合里的意思就是:

如果\(x\)在集合里,那么二进制意义下,\(x\)的末尾加一个\(1\)和加两个\(0\)代表的数字也在集合里。

不超过\(2^p\)的数字就是说所有数字在二进制意义下不超过\(p\)

那么我们假设\(x\)在二进制意义下有\(k\)

那么它带来的\(k+1\)位数字就有\(1\)\(k+2\)位数字有\(1\)个,\(k+3\)位数字有\(2\)个,\(k+4\)位数字有\(3\)个,\(k+5\)位数字有\(5\)个。

这就是一个斐波那契的递推式!它带来的贡献就是\(\sum_{i=k}^{p}f[i-k]\),其中\(f[i]\)是斐波那契数列,这个前缀和可以预处理得到。

但是有重复的,比如\(10011\)\(1001\)就会重复贡献,因为\(10011\)可以通过\(1001\)得到。

那我们就先把数字排序,然后对一个数字一直做逆操作

比如\(10011->1001->100->1\)

如果这个过程中某个数字已经出现过了,那就说明这个数字被表示了,直接跳过这个数字。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
    const int N=3e5+10,mod=1e9+7,inf=2e9;
    int n,m,ans;
    int a[N];
    int f[N],g[N];
    map<int,int> q;
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T=1;
        f[0]=f[1]=1;
        g[0]=1;
        for(int i=2;i<=2e5;++i) f[i]=(f[i-1]+f[i-2])%mod;
        for(int i=1;i<=2e5;++i) g[i]=(g[i-1]+f[i])%mod;
        while(T--)
        {
            cin>>n>>m;ans=0;
            q.clear();
            for(int i=1;i<=n;++i) cin>>a[i];
            sort(a+1,a+n+1);
            for(int i=1;i<=n;++i)
            {
                int x=a[i];
                int sum=0;
                while(x) ++sum,x>>=1;
                x=a[i];
                bool flag=0;
                while(x)
                {
                    if(q[x]){flag=1;break;}
                    if(x%4==0) x>>=2;
                    else if(x%2!=0) x>>=1;
                    else break;
                }
                q[a[i]]=1;
                if(flag) continue;
                if(sum<=m) ans=(ans+g[m-sum])%mod;
            }
            cout<<ans<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;   
}
/*

*/

E

题意:

数轴上有\(n\)辆车,有\(m\)个两辆车之间的关系:

关系一:无论车\(x\)\(y\)速度是多少,二者都无法相遇

关系二:无论车\(x\)\(y\)速度是多少,二者都会相遇

构造一种满足\(m\)个关系的方案,或者输出无法满足。

解答:

关系一表示两者背道而驰,关系二表示二者相向而行。都表示车\(x\)\(y\)方向相反。所以可以先二分图染色,如果无法染色成功就一定不行。

染色之后,要确定方案,假设黑色点是向左的,白色点是向右的,那么关系一表示,黑色点在白色点左边,关系二表示,黑色点在白色点的右边。

根据左右关系建立一个拓扑图,图上一条边\(x->y\)表示\(x\)\(y\)的左边,然后根据拓扑序确定每辆车的顺序。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m;
    bool flag=1;
    vector<int> eg[N];
    vector<int> g[N];
    int col[N],rd[N];
    queue<int> q;
    struct edge
    {
        int opt,x,y;
    }e[N];
    int ans[N],idx;
    inline void dfs(int now,int c)
    {
        col[now]=c;
        for(auto t:eg[now])
        {
            if(col[t])
            {
                if(col[t]==col[now]) flag=0;
                continue;
            }
            dfs(t,c^1);
        }
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n>>m;
        for(int i=1;i<=m;++i)
        {
            int opt,x,y;cin>>opt>>x>>y;
            e[i]=(edge){opt,x,y};
            eg[x].push_back(y);
            eg[y].push_back(x);
        }
        for(int i=1;i<=n;++i)
        {
            if(!col[i]) dfs(i,2);
        }
        for(int i=1;i<=m;++i)
        {
            if(col[e[i].x]==3) swap(e[i].x,e[i].y);
            if(e[i].opt==1) g[e[i].x].push_back(e[i].y),++rd[e[i].y];
            else g[e[i].y].push_back(e[i].x),++rd[e[i].x];
        }
        for(int i=1;i<=n;++i) if(!rd[i]) q.push(i);
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            ans[now]=++idx;
            for(int t:g[now])
            {
                --rd[t];
                if(!rd[t]) q.push(t);
            }
        }
        for(int i=1;i<=n;++i) if(!ans[i]) flag=0;
        if(!flag) cout<<"NO\n";
        else
        {
            cout<<"YES\n";
            for(int i=1;i<=n;++i)
            {
                if(col[i]==2) cout<<"L ";
                else cout<<"R ";
                cout<<ans[i]<<'\n';
            }
        }
    }
}
signed main()
{
    red::main();
    return 0;   
}
/*

*/

F

题意:

数轴上给出\(n\)个点的坐标\(x_i\)和权值\(w_i\),一对点选择的代价是\(|x_i-x_j|*(w_i+w_j)\),每次询问给定\([l,r]\),问在\(l,r\)中选一对点的最小代价是多少?

解答:

首先,假设\(i\)左边权值第一个小于等于\(w_i\)的点是\(L_i\),右边第一个权值小于等于\(w_i\)的点是\(R_i\),那么最终的答案一定是\((L_i,i),(i,R_i)\)\(2n\)对点中的某些点。

因为如果选择\((x,i)\)\(w_x<=w_i\),且\(x\neq L_i\)那么去选择\((x,L_i)\)肯定更优。

这样问题转化成了,每次给定一个区间\([l,r]\),选择一条被\([l,r]\)完全包含的代价最小的带权线段。就是扫描线裸题了。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m;
    int a[N],w[N];
    int tl[N],tr[N],st[N],top;
    int ret[N];
    struct node
    {
        int l,r,id;
        inline bool operator < (const node &t) const
        {
            return r<t.r;
        }
    }q[N];
    vector<node> v[N];
    struct segment_tree
    {
        int ans[N<<2];
        inline void init()
        {
            memset(ans,0x3f,sizeof(ans));
        }
        inline void update(int pos,int l,int r,int p,int k)
        {
            ans[p]=min(ans[p],k);
            if(l==r) return;
            if(pos<=mid) update(pos,l,mid,ls(p),k);
            else update(pos,mid+1,r,rs(p),k);
        }
        inline int query(int tl,int tr,int l,int r,int p)
        {
            if(tl<=l&&r<=tr) return ans[p];
            int ret=2e18;
            if(tl<=mid) ret=min(ret,query(tl,tr,l,mid,ls(p)));
            if(tr>mid) ret=min(ret,query(tl,tr,mid+1,r,rs(p)));
            return ret;
        }
    }T;
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int qaq=1;
        while(qaq--)
        {
            cin>>n>>m;
            for(int i=1;i<=n;++i)
            {
                cin>>a[i]>>w[i];
                while(top&&w[st[top]]>=w[i])
                {
                    tr[st[top--]]=i;
                }
                st[++top]=i;
            }
            while(top) tr[st[top--]]=n;
            for(int i=n;i;--i)
            {
                while(top&&w[st[top]]>=w[i])
                {
                    tl[st[top--]]=i;
                }
                st[++top]=i;
            }
            while(top) tl[st[top--]]=1;
            for(int i=1;i<=n;++i)
            {
                if(i-tl[i]>0) v[i].push_back((node){tl[i],(a[i]-a[tl[i]])*(w[i]+w[tl[i]])});
                if(tr[i]-i>0) v[tr[i]].push_back((node){i,(a[tr[i]]-a[i])*(w[i]+w[tr[i]])});
            }
            for(int i=1;i<=m;++i)
            {
                cin>>q[i].l>>q[i].r;
                q[i].id=i;
            }
            sort(q+1,q+m+1);
            T.init();
            int now=1;
            for(int i=1;i<=n;++i)
            {
                for(auto t:v[i])
                {
                    T.update(t.l,1,n,1,t.r);
                }
                while(now<=m&&q[now].r<=i)
                {
                    ret[q[now].id]=T.query(q[now].l,q[now].r,1,n,1);
                    ++now;
                }
            }
            for(int i=1;i<=m;++i) cout<<ret[i]<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;   
}
/*

*/
posted @ 2022-02-21 19:12  lovelyred  阅读(68)  评论(0)    收藏  举报