Codeforces Round #736 (Div. 2) 题解
传送门:https://codeforces.com/contest/1549
A
int main(){
int T; cin>>T;
while(T--){
int x; cin>>x;
cout<<2<<' '<<x-1<<endl;
}
return 0;
}
B
贪心,能直走就直走,因为是从左到右扫过去,所以优先吃左边的,如果左边没有右边有就吃右边的。
int main(){
int T; cin>>T;
while(T--){
int n; cin>>n;
string a, b; cin>>a>>b;
a='#'+a+'#', b=' '+b;
int res=0;
rep(i,1,n) if(b[i]=='1'){
if(a[i]=='0'){
a[i]='#';
res++;
continue;
}
if(a[i-1]=='1'){
a[i-1]='#';
res++;
continue;
}
if(a[i+1]=='1'){
a[i+1]='#';
res++;
continue;
}
}
cout<<res<<endl;
}
return 0;
}
C
维护 \(n\) 个点对应的集合。
注意到如果一个点它自己的集合内存在比它编号大的元素,那么它就会被干掉。
所以我们先通过初始关系维护一个初始的存活集合个数 \(cur\),然后对于每次查询我们都更新一下发生修改的集合以及 \(cur\) 即可。
#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define debug(x) cerr << #x << ": " << x << endl
#define pb(a) push_back(a)
#define set0(a) memset(a,0,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define ceil(a,b) (a+(b-1))/b
#define INF 0x3f3f3f3f
#define ll_INF 0x7f7f7f7f7f7f7f7f
typedef long long ll;
typedef pair<int,int> PII;
typedef pair<double,double> PDD;
inline void read(int &x) {
int s=0;x=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
x*=s;
}
const int N=2e5+50;
set<int> g[N];
int main(){
int n, m; cin>>n>>m;
rep(i,1,m){
int u, v; read(u), read(v);
g[u].insert(v);
g[v].insert(u);
}
int cur=0;
rep(i,1,n) if(g[i].size()){
if(*g[i].rbegin()<i) cur++;
}else cur++;
int q; cin>>q;
while(q--){
int op; read(op);
if(op==1){
int u, v; read(u), read(v);
if(u>v) swap(u, v);
bool ok=1;
if(g[u].size() && *g[u].rbegin()>u) ok=0;
g[u].insert(v);
g[v].insert(u);
cur-=ok;
}
else if(op==2){
int u, v; read(u), read(v);
if(u>v) swap(u, v);
g[u].erase(v);
g[v].erase(u);
if(!g[u].size()) cur++;
else if(*g[u].rbegin()<u) cur++;
}
else cout<<cur<<endl;
}
return 0;
}
D
假设选取了 \([l, r]\) 这一段,那么这一段是满足题意的子段当且仅当 \(gcd_{i=l+1}^r d_i\neq1\),其中 \(d\) 代表序列 \(d_{i}-d_{i-1}\) (\(i\in [2,n]\))。
所以我们的做法是用 st 表维护一下区间 \(gcd\) 值,然后枚举序列 \(d\) 左端点,看看右端点最远的位置使得 \(gcd\) 值不为 \(1\),这是满足二段性的所以可以用二分来做。
当然,如果得到区间长度至少为 \(1\),如果这个数 \(gcd\) 本来就是 \(1\) 就要跳过(不更新答案)。
#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define debug(x) cerr << #x << ": " << x << endl
#define pb(a) push_back(a)
#define set0(a) memset(a,0,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define ceil(a,b) (a+(b-1))/b
#define INF 0x3f3f3f3f
#define ll_INF 0x7f7f7f7f7f7f7f7f
typedef long long ll;
typedef pair<int,int> PII;
typedef pair<double,double> PDD;
#define int long long
inline void read(int &x) {
int s=0;x=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
x*=s;
}
const int N=6e5+5, M=25;
int w[N], d[N];
int n, logn[N<<2];
int st[N][M];
int gcd(int a, int b){
return b? gcd(b, a%b): a;
}
void init(){
for(int i=2;i<=n;i++) logn[i]=logn[i/2]+1;
for(int j=0;j<M;j++)
for(int i=2;i+(1<<j)-1<=n;i++)
if(!j) st[i][j]=d[i];
else st[i][j]=gcd(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
int query(int l,int r){
int len=r-l+1;
int k=logn[len];
return gcd(st[l][k],st[r-(1<<k)+1][k]);
}
signed main(){
int T; cin>>T;
while(T--){
read(n);
rep(i,1,n) read(w[i]);
rep(i,2,n) d[i]=w[i]-w[i-1];
init();
int res=1;
rep(i,2,n){
int l=i, r=n;
while(l<r){
int mid=l+r+1>>1;
if(abs(query(i, mid))!=1) l=mid;
else r=mid-1;
}
if(abs(query(i, i))==1) continue;
else res=max(res, l-i+1+1);
}
cout<<res<<endl;
}
return 0;
}