Codeforces Round 877 (Div. 2)
A. Blackboard List
题意:
黑板上写着两个整数。之后,进行以下步骤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
题意:将一个数组交换任意两个位置,让其删去首或尾几个元素让子数组个数尽量少,
思路:让最大值在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
题意:你得到整数n和m填写一个n由m带有整数的网格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
思路:
这题要画图,看样例可以得出()是无论啥时候一定合法的
所以我们直接按照下标来插入不合法的下标
(为偶,)为奇数合法,其他不合法直接插入
如果当前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
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
上面是书上的题解,整体来说就是将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(超大数值)
上面和幂的方法类似,都是讲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路径
状态压缩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;
}