文章目录
A.Mocha and Math
题意: 每次操作可以选择一个区间【l,r】可以使得改区间内a[i]变成a[i]&a[r+l-i],可以进行无数次操作,问最终得到a数组的最小值
思路: 对于一个i来说,必然存在一个区间[l,r]可以使得该数可以和任意一个j可以相互&,因此遍历一遍数组即可
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
int T;
int a[maxn];
int main()
{
int n;
cin>>T;
while(T--){
cin>>n;for(int i=1;i<=n;i++) cin>>a[i];
int maxx=a[1];
for(int i=1;i<=n;i++) maxx&=a[i];
printf("%d\n",maxx);
}
return 0;
}
B.Mocha and Red and Blue
题意: 给一个字符串,包含’B’‘R’’?’,其中’?‘处可以填’B’和’R’,问如何填可以使得’BB’'RR’的个数最少
思路:
贪心尽量使得每个B和R交替
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int T;
int a[maxn];
string s;
int main()
{
int n;
cin>>T;
while(T--){
cin>>n;
cin>>s;
int fir=-1;
for(int i=0;i<s.size();i++){
if(s[i]!='?'){fir=i;break;}
}
if(fir==-1){
for(int i=0;i<s.size();i++){
if(i%2) s[i]='B';else s[i]='R';
}
cout<<s<<endl;continue;
}
for(int i=fir-1;i>=0;i--){
if(s[i+1]=='B') s[i]='R';
else s[i]='B';
}
for(int i=fir+1;i<s.size();i++){
if(s[i]=='?'){
if(s[i-1]=='B') s[i]='R';else s[i]='B';
}
}
cout<<s<<endl;
}
return 0;
}
C.Mocha and Hiking
题意: 给n个点,2n-1条边,1到n是一条链,同时给一个a数组,a[i]=0表示i到n+1有一条边,a[i]=1表示n+1到i有一条边,从任意点出发,任意点结束,求保证每个点只经过一次的路径
思路: 由于1到n是一条链,所以我们只需考虑如何能够经过n+1这个点就可以了,显然n+1这个点只能放在首末或者中间出现一次
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int T;
int a[maxn];
string s;
int main()
{
int n;
cin>>T;
while(T--){
cin>>n;int x;
for(int i=1;i<=n;i++) cin>>a[i];
int f=0;
for(int i=1;i<=n-1;i++){
if(a[i]==0&&a[i+1]==1)
{
f=i;break;
}
}
if(!f) {
if(a[n]==0) f=1;
if(!f){
if(a[1]==1) f=1;
if(!f){printf("-1\n");continue;}
else {
printf("%d ",n+1);
for(int i=1;i<=n;i++) printf("%d ",i);printf("\n");continue;
}
}
for(int i=1;i<=n+1;i++) printf("%d ",i);printf("\n");continue;
}
for(int i=1;i<=n;i++)
{
printf("%d ",i);
if(f==i){
printf("%d ",n+1);
}
}
printf("\n");
}
return 0;
}
D1.Mocha and Diana (Easy Version)
题意: 给两个森林,可以进行的操作是对两个图同时加u,v这条边可以使得两个图在操作完后仍然是森林
思路: 在构建完森林以后直接n^2跑,判断是否可以连接即可
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll>P;
const int maxn=1e6+10;
int n,m1,m2;
int fa1[maxn],fa2[maxn];
int _find1(int x){
if(x==fa1[x]) return x;
else return fa1[x]=_find1(fa1[x]);
}
int _find2(int x){
if(x==fa2[x]) return x;
else return fa2[x]=_find2(fa2[x]);
}
int main()
{
cin>>n>>m1>>m2;
vector<P> ans;
for(int i=1;i<=n;i++) fa1[i]=fa2[i]=i;
int u,v;
while(m1--){
cin>>u>>v;
int x=_find1(u),y=_find1(v);
if(x!=y){
fa1[x]=y;
}
}
while(m2--){
cin>>u>>v;
int x=_find2(u),y=_find2(v);
if(x!=y){
fa2[x]=y;
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
int x=_find1(i),y=_find1(j);
int xx=_find2(i),yy=_find2(j);
if(x!=y&&xx!=yy){
fa1[x]=y;fa2[xx]=yy;
ans.push_back(P(i,j));
}
}
}
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++){
printf("%d %d\n",ans[i].first,ans[i].second);
}
return 0;
}
D2.Mocha and Diana (Hard Version)
思路一:并查集+技巧
已知两个点能并上的前提是fa1[i]!=fa1[j]&&fa2[i]!=fa2[j]
那么我们强行让1和其他所有点满足这个条件的先连上
那么就会变成这样两个图(圆表示一个森林)
然后再分别找不在图1和图2的包含1的连通块把他们相连即可
原因如下:
图中i表示的是图1中找到的不在1中的连通块
j表示的是图2中找到的不在1中的连通块
显然i在图2中在1的连通块内
j在图1中也在1的连通块内
因此,只要把i和j连接起来就是答案了
代码:
#include<bits/stdc++.h>
#define ls k<<1
#define rs k<<1|1
#define pb push_back
using namespace std;
const int maxn=1e6+100;
const int MAXNE=2e5+100;
typedef long long ll;
typedef pair<int,int>P;
struct
{
int f[maxn];
int _find(int x){
if(x==f[x]) return x;
else return f[x]=_find(f[x]);
}
void _merge(int x,int y){
int fa1=_find(x),fa2=_find(y);
if(fa1!=fa2){
f[fa1]=fa2;
}
}
}T1,T2;
int a[maxn],b[maxn],ans=0;
int main(){
int n,m1,m2;
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1;i<=n;i++) T1.f[i]=T2.f[i]=i;
int x,y;
while(m1--){
scanf("%d%d",&x,&y);
T1._merge(x,y);
}
while(m2--){
scanf("%d%d",&x,&y);
T2._merge(x,y);
}
for(int i=2;i<=n;i++){
if(T1._find(1)!=T1._find(i)&&T2._find(1)!=T2._find(i)){
a[++ans]=1;b[ans]=i;
T1._merge(1,i);T2._merge(1,i);
}
}
int s1=ans,s2=ans;
for(int i=2;i<=n;i++){
if(T1._find(1)!=T1._find(i)){
a[++s1]=i;
T1._merge(1,i);
}
if(T2._find(1)!=T2._find(i)){
b[++s2]=i;
T2._merge(1,i);
}
}
ans=min(s1,s2);
printf("%d\n",ans);
for(int i=1;i<=ans;i++) printf("%d %d\n",a[i],b[i]);
return 0;
}
思路二:启发式合并
已知两个点能并上的前提是fa1[i]!=fa1[j]&&fa2[i]!=fa2[j]
那么我们对一个i,构造一个二元组(fa1[i],fa2[i]),将其对应到二维平面上我们会发现,i和j可以连接的前提是i和j的二元组满足图中关系
在i和j合并后图会变成这样(也就是i和j的行列合并了)
由此我们可以想到用启发式合并来实现将小的行/列并到大的行/列中去
代码:
#include<bits/stdc++.h>
#define ls k<<1
#define rs k<<1|1
#define pb push_back
using namespace std;
const int maxn=1e6+100;
const int MAXNE=2e5+100;
typedef long long ll;
typedef pair<int,int>P;
int f1[maxn],f2[maxn];
int findrow(int x){
if(x==f1[x]) return x;
else return f1[x]=findrow(f1[x]);
}
int findcol(int x){
if(x==f2[x]) return x;
else return f2[x]=findcol(f2[x]);
}
void mergerow(int x,int y){
int fa1=findrow(x),fa2=findrow(y);
if(fa1!=fa2){
f1[fa1]=fa2;
}
}
void mergecol(int x,int y){
int fa1=findcol(x),fa2=findcol(y);
if(fa1!=fa2){
f2[fa1]=fa2;
}
}
map<int,int> tu[maxn];
set<int> row[maxn],col[maxn];
set<P> Set;
set<int> ::iterator it;
int main(){
int n,m1,m2;
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1;i<=n;i++) f1[i]=f2[i]=i;
int x,y;
for(int i=0;i<m1;i++){
scanf("%d%d",&x,&y);
mergerow(x,y);
}
for(int i=0;i<m2;i++){
scanf("%d%d",&x,&y);
mergecol(x,y);
}
if(m1<m2) swap(f1,f2);
for(int i=1;i<=n;i++){
int x=findrow(i),y=findcol(i);
tu[x][y]=i;
row[x].insert(y);
col[y].insert(x);
}
for(int i=1;i<=n;i++){
if(i==findrow(i)){
Set.insert(make_pair(-row[i].size(),i));
}
}
vector<P> res;
while(Set.size()>=2){
int x=Set.begin()->second;
Set.erase(Set.begin());
int y=Set.begin()->second;
Set.erase(Set.begin());
// printf("x = %d, y = %d\n",x,y);
if(row[x].size()<row[y].size()){
swap(x,y);
}
it=row[x].begin();
int a=*it,b=*(row[y].begin());
if(a==b){
a=*(++it);
}
res.push_back(make_pair(tu[x][a],tu[y][b]));
if(col[a].size()<col[b].size()) swap(a,b);
for(it=row[y].begin();it!=row[y].end();it++){
tu[x][*it]=tu[y][*it];
row[x].insert(*it);
col[*it].erase(y);
col[*it].insert(x);
}
for(it=col[b].begin();it!=col[b].end();it++){
tu[*it][a]=tu[*it][b];
col[a].insert(*it);
row[*it].erase(b);
row[*it].insert(a);
}
Set.insert(make_pair(-row[x].size(),x));
}
printf("%d\n",res.size());
for(P x:res){
printf("%d %d\n",x.first,x.second);
}
return 0;
}