Codeforces Round 877 (Div. 2)补题+晚上新学的(位运算)

文章详细解析了CodeforcesRound877(Div.2)中的四个问题,包括黑板列表恢复、最小化子数组、无质数差值填充和括号序列合法性判断。解题策略涉及数组处理、最大值定位、位运算以及动态规划等算法思想。
摘要由CSDN通过智能技术生成

Codeforces Round 877 (Div. 2)

A. Blackboard List

Problem - A - Codeforces

题意:

黑板上写着两个整数。之后,进行以下步骤n−2次:

选择板上的任意两个整数,并将其差值的绝对值写在板上。

在这个过程完成后,n个整数的列表被打乱。你得到了最后的名单。恢复最初两个数字中的一个。您不需要恢复另一个。

我个人感觉没说清楚:比如如果三个负数必然凑不出另一个负数

题解:当全是正数时取最大的那个正数,当即含有负数又含有正数时输出最大的那个负数

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
const int N=1e6+7;
int a[N],b[N],s[N];
bool st[N];
int res=10,ans=0;
void solve(){
	int n;
	cin>>n;
	int f=0;
	for(int i=0;i<n;i++){
		cin>>a[i];
		if(a[i]<0)f=1;
	}
	sort(a,a+n);
	if(!f)cout<<a[n-1]<<endl; 
	else {
		for(int i=0;i<n-1;i++){
			if(a[i]<0&&a[i+1]>=0){
				cout<<a[i]<<endl;
				return ;
			}
		}
	}
}

int main(){
	 int t;
	 cin>>t;
	 while(t--)solve();
}

B. Minimize Permutation Subarrays

Problem - B - Codeforces

题意:将一个数组交换任意两个位置,让其删去首或尾几个元素让子数组个数尽量少,

思路:让最大值在1和2之间就可以满足条件了,为什么呢?

当1和2在同侧时:1,当1 2想连时,至少有12者个子数组

                              2,当1 2不相连时,右边也可能存在能让1 2组合在一起,例如1 3 4 2

当1和2中间存在某个数时:要1和2想连成子数组必须满足要在某个区间内包含1到中间这个值,所有,当中间这个值等于最大值时只有全部包含才能满足

所有1和2中间必须包含最大值才是最优

#include<bits/stdc++.h>
using namespace std;
int main(){
ios_base::sync_with_stdio(false); cin.tie(NULL);
int t = 1;
cin >> t;
while(t--){
int n; cin>>n;
vector<int>v(n);
for(int&x : v) cin>>x;
vector<int>u;
for(int i=0; i<v.size(); i++){
if(v[i]<=2 || v[i]==n) u.emplace_back(i);
}
for(int x : u) if(v[x]==n) cout<<u[1]+1<<" "<<x+1<<'\n';
}
}

C. No Prime Differences

Problem - C - Codeforces

题意:你得到整数nm填写一个nm带有整数的网格11通过n⋅m,对于网格中的任何两个相邻像元,这些像元中值的绝对差不是质数。如果网格中的两个像元共享一侧,则它们被视为相邻。可以证明,在给定的约束下,总有一个解决方案

思路:规律题,其实很容易想到条件一:横向相差1,条件二:纵向相差偶数就行,如何找呢?

1,直接横向顺序输出就满足横向相差1了

2,这里其实要分奇偶行的,当为偶数行时先输出奇数行或者偶数行都行,当为奇数行时必须先输出偶数行,因为奇数行必定比偶数行多一行,只能放到最后才满足

所有直接先输出偶数行就行

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	cin>>t;
	while (t--){
		int n,m;
		cin>>n>>m;
		//偶数行
		for (int i=1;i<n;i+=2){
			for (int j=1;j<=m;j++){
				printf("%d ",i*m+j);
			}
			cout<<endl;
		}
		//奇数行
		for (int i=0;i<n;i+=2){
			for (int j=1;j<=m;j++){
				printf("%d ",i*m+j);
			}
			cout<<endl;
		}
	}
}  

D. Bracket Walk

Problem - D - Codeforces

思路:

这题要画图,看样例可以得出()是无论啥时候一定合法的

所以我们直接按照下标来插入不合法的下标

(为偶,)为奇数合法,其他不合法直接插入

如果当前set里面的空的,那么就是一个合法序列相等于()()()()全是这种

然后其实中间你可以通过往返走处理但是有些情况是处理不了得

比如第一个不合法下标是),如果那么在这个不合法下标左边得括号你无论怎么反复横跳都没用

比如()) 和(()))你都无法处理这个) 

最后一个不合法下标是(,因为你要保证中间合法,()(和(())(你也无法处理


 

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include<functional>
using namespace std;
const int N = 1010,mod=1e9+7;
//define int long long
typedef pair<int, int> PII;
int n,m,k;
int a[N][N];
bool check(int n){
    if(n<=3) return n>1;
    if(n%6!=1&&n%6!=5) return false;
    for(int i=5;i<=n/i;i+=6){
        if(n%i==0||n%(i+2)==0) return false;
    }
    return true;
}
void solve(){
    set<int> st;
    string s;
    cin>>n>>m>>s;
    for(int i=0;i<n;i++){
        if(i%2==1&&s[i]=='(') st.insert(i);
        if(i%2==0&&s[i]==')') st.insert(i);
    } for(auto&x:st) cout<<x<<" ";
        cout<<"\n";
    while (m -- ){
        int x;
        cin>>x;
        x--;
        if(st.count(x)) st.erase(x);
        else st.insert(x);
        for(auto&x:st) cout<<x<<" ";
        cout<<"\n";
        if(n&1) cout<<"NO\n";
        else if(!st.empty()&&(*st.begin()%2==0||*st.rbegin()%2==1)){
            cout<<"NO\n";
        }
        else cout<<"YES\n";
    }
}
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    
    int t=1;
    //cin>>t;
    while(t--) solve();
}

E. Count Supersequences

Problem - E - Codeforces

m的每个位置可以放k个减去恰好有i(0<=i<=n-1)的位置放a里面的数(如果放n个那就是合法的答案,这样会减多了)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include<functional>
using namespace std;
const int N = 2e5+10,mod=1e9+7;
typedef long long LL;
typedef pair<int, int> PII;
int n,m,k;
int a[N];
LL fact[N],infact[N];
int qmi(int a, int k, int p)  // 求a^k mod p
{
    int res = 1 % p;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}
void init()
{
    fact[0]=infact[0]=1;
    for(int i=1;i<N;i++){
        fact[i]=(LL)fact[i-1]*i%mod;
        infact[i]=(LL)infact[i-1]*qmi(i,mod-2,mod)%mod;
    }
}
void solve(){
    cin>>n>>m>>k;
    for(int i=0;i<n;i++) cin>>a[i];
    if(k==1){
        cout<<1<<"\n"; 
        return ;
    }
    LL res=0;
    LL cnt=1;
    for(int i=0;i<n;i++)
    {
        res=(res+cnt*qmi(k-1,m-i,mod))%mod;
        cnt=cnt*(m-(i+1)+1)%mod*qmi(i+1,mod-2,mod)%mod;
    }
    cout<<(qmi(k,m,mod)+mod-res)%mod<<"\n";
    
}
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    init();
    cin>>t;
    while(t--) solve();
}

位运算

1,取出整数n在二进制表示下的第k位   (n>>k)&1

2,取出整数n在二进制表示下的第0~k-1位(后k位)  n&((1<<k)-1)

3,把整数n在二进制表示下的第k位取反  n xor (1<<k)

4,对整数n在二进制表示下的k位赋值1  n|(1<<k)

5,对整数n在二进制表示下的第k位赋值0  n&(~(1<<k))

一,a^b

89. a^b - AcWing题库

 

 上面是书上的题解,整体来说就是将b进行二进制化,在让a与b进行幂运算,当b的某一位是1的时候进行所求答案乘a,然后b进行右移,a进行平方取余

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int power(int a,int b,int p){

    int ans=1%p;//确保b为0
    while(b){
        //当前位是否是1
        if(b&1)ans=(long long)ans*a%p;
        b>>=1;
        a=(long long)a*a%p;
    }
    return ans;
}
int main(){
    int a,b,p;
    cin>>a>>b>>p;
    cout<<power(a,b,p);
}

 

 

二,a*b(超大数值)

90. 64位整数乘法 - AcWing题库

 

上面和幂的方法类似,都是讲b进行二进制化,在让a与b进行乘法运算,当b的某一位是1的时候进行所求答案加a,然后b进行右移,a进行乘2取余

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll  mul(ll a,ll b,ll p){
    ll ans=0;
    while(b){
        if(b&1)ans=(ans+a)%p;
        b>>=1;
        a=a*2%p;
    }
    return ans;
}
int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    cout<<mul(a,b,p);
}

三,最短Hamilton路径

91. 最短Hamilton路径 - AcWing题库

 

状态压缩dp:

用二进制来表示要走的所以情况的路径,这里用i来代替
例如走0,1,2,4这三个点,则表示为:10111;
走0,2,3这三个点:1101;
状态表示:f[i][j];
集合:所有从0走到j,走过的所有点的情况是i的所有路径
属性:MIN
状态计算:如1中分析一致,0–>·····–>k–>j中k的所有情况

状态转移方程:f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j])

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=20,M=1<<N;

int f[M][N],w[N][N];//w表示的是无权图

int main()
{
    int n;
    cin>>n;

    for(int i=0;i<n;i++)
     for(int j=0;j<n;j++)
      cin>>w[i][j];

    memset(f,0x3f,sizeof(f));//因为要求最小值,所以初始化为无穷大
    f[1][0]=0;//因为零是起点,所以f[1][0]=0;

    for(int i=0;i<1<<n;i++)//i表示所有的情况
     for(int j=0;j<n;j++)//j表示走到哪一个点
      if(i>>j&1)
       for(int k=0;k<n;k++)//k表示走到j这个点之前,以k为终点的最短距离
        if(i>>k&1)
         f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j]);//更新最短距离

    cout<<f[(1<<n)-1][n-1]<<endl;//表示所有点都走过了,且终点是n-1的最短距离
    //位运算的优先级低于'+'-'所以有必要的情况下要打括号
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linkk_bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值