2021牛客暑期多校训练营4
B.Sample Game
概率dp,挖坑
LCS
按LCS大小顺序,模拟即可,代码不美观就不发了。
E.Tree Xor
思路:
可知我们先令 W i = 0 W_i=0 Wi=0,可得节点 W i = { W 1 , W 2 . . . W n } W_i=\{W_1,W_2...W_n\} Wi={W1,W2...Wn} 。
易知当 W 1 x o r X W_1\ xor\ X W1 xor X,之后所以 W i W_i Wi都会异或上X,那么我们需计算以下不等式。
L i ≤ W i x o r X ≤ R i L_i\le W_i\ xor\ X\le R_i Li≤Wi xor X≤Ri,X能取的合法值。
即 L i x o r W i ≤ X ≤ R i x o r W L_i\ xor\ W_i\le X\le R_i\ xor\ W Li xor Wi≤X≤Ri xor W。
这样我们只需求 [ L i , R i ] x o r W i [L_i,R_i]xor\ W_i [Li,Ri]xor Wi的区间交,即可算出答案。
线段树维护
我们可能看出, [ L i , R i ] x o r W i [L_i,R_i]\ xor\ W_i [Li,Ri] xor Wi会被分割成若干个不连续的区间。通过二进制的异或个规律我们可以发现,如果我们建棵 [ 0 , 2 30 − 1 ] [0,2^{30}-1] [0,230−1]的线段树区间,那么线段树分割的区间将全为连续的区间。那么我们用线段树求出异或后的区间,最后求区间交算答案。
代码:
#include <iostream>
#include <algorithm>
#include <vector>
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pll;
const int N=1e5+7;
const int S=(1<<30)-1;//建区间为[0,2^30-1]的线段树
struct node{int to,w,next;}e[2*N];
int n,a[N],ll[N],rr[N];
int head[N],tot;
vector <pll> ho,col;
void add(int u,int v,int w){
e[++tot].to=v;
e[tot].w=w;
e[tot].next=head[u];
head[u]=tot;
}
void dfs(int p,int fa){
for(int i=head[p];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(v==fa) continue;
a[v]=a[p]^w;
dfs(v,p);
}
}
//处理[l,r] xor X
void ikuzo(int l,int r,int d,int x){
//d为有多少位值不同。
int pl=l&(S^((1<<d)-1));
int pe=x&(S^((1<<d)-1));
ho.push_back(pll(pe^pl,pe^pl+((1<<d)-1)));
}
void update(int l,int r,int ql,int qr,int d,int x){
if(ql<=l&&r<=qr){
ikuzo(l,r,d,x);
return;
}
int mid=(l+r>>1);
if(ql<=mid) update(l,mid,ql,qr,d-1,x);
if(mid+1<=qr) update(mid+1,r,ql,qr,d-1,x);
}
void solve(){
//差分求区间交
for(int i=0;i<ho.size();i++){
col.push_back(pll(ho[i].fi,1));
col.push_back(pll(ho[i].se+1,-1));
}
sort(col.begin(),col.end());
int sum=0,ans=0;
for(int i=0;i+1<col.size();i++){
sum+=col[i].se;
if(sum>=n){
ans+=col[i+1].fi-col[i].fi;
}
}
printf("%d\n",ans);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>ll[i]>>rr[i];
for(int i=1;i<=n-1;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);add(v,u,w);
}
dfs(1,-1);
for(int i=1;i<=n;i++){
update(0,S,ll[i],rr[i],30,a[i]);
}
solve();
}
F.Just a joke
思路:
通过并查集算环和连通块。
假设连通块无环,即消除需奇数次。
假设连通块有环,为1+连通块环数。
我们算出连通块数+环数判奇偶即可算出谁必胜
代码:
#include <iostream>
using namespace std;
const int N=1e3+7;
int n,m,f[N];
void init(){
for(int i=1;i<=n;i++){
f[i]=i;
}
}
int fin(int x){return f[x]==x? x:f[x]=fin(f[x]);}
int bing(int x,int y){
int f1=fin(x),f2=fin(y);
if(f1==f2) return 1;
f[f2]=f1;
return 0;
}
int main(){
cin>>n>>m;
init();
int cnt=0;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
if(bing(u,v)){
cnt++;
}
}
for(int i=1;i<=n;i++){
if(fin(i)==i) cnt++;
}
if(cnt&1) cout<<"Alice\n";
else cout<<"Bob\n";
}
I.Inverse Pair
思路:
我们可知,由于序列是个排列,所以我们对原序列进行+1,只能让原序列序列逆序对-1,即序列多出一对相等的数,所以我们从前往后遍历,如果序列+1能与之前某个元素相等,那么就逆序对-1。
代码:
#include <iostream>
using namespace std;
typedef long long ll;
const int N=2e5+7;
const int S=2e5;
int n,a[N],tree[N],vis[N];
int lowbit(int i){return i&(-i);}
void add(int p){
for(int i=p;i<=S;i+=lowbit(i)){
tree[i]++;
}
}
int ask(int p){
int res=0;
for(int i=p;i;i-=lowbit(i)){
res+=tree[i];
}
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
ll res=0;
for(int i=n;i>=1;i--){
res+=ask(a[i]);
add(a[i]);
}
for(int i=1;i<=n;i++){
if(vis[a[i]+1]){
res--;
}else{
vis[a[i]]++;
}
}
cout<<res<<"\n";
}
J.Average
思路:
题目问求 W i , j W_{i,j} Wi,j矩阵的长宽至少为 X , Y X,Y X,Y子矩阵的最大平均值,我们可以发现其平均值为 a i a_i ai的子序列长度至少为X的最大平均值与 b i b_i bi的子序列长度至少为Y的最大平均值之和。
二分答案
我们可以通过二分最大平均值答案,假设二分答案为mid
之后建立 v i = a i − m i d v_i=a_i-mid vi=ai−mid的前缀和,算出 M a x { p r e i − p r e j } Max\{ pre_i-pre_j\} Max{prei−prej},且 i − j i-j i−j大于至少的长度限制。
如果大于等于0,则返回 t r u e true true。
代码:
#include <iostream>
using namespace std;
const int N=1e5+7;
int n,m,x,y,a[N],b[N];
double v[N];
bool check(int a[],int sz,double mid,int p){
for(int i=1;i<=sz;i++){
v[i]=v[i-1]+double(a[i]-mid);
}
double mn=1e10,ans=-1e10;
for(int i=p;i<=sz;i++){
mn=min(mn,v[i-p]);
ans=max(ans,v[i]-mn);
}
return ans>=0;
}
double solve(int a[],int sz,int p){
double l=0,r=100000,ans=0;
for(int i=0;i<=100;i++){
double mid=(l+r)/2.0;
if(check(a,sz,mid,p)) ans=mid,l=mid;
else r=mid;
}
return ans;
}
int main(){
cin>>n>>m>>x>>y;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d",&b[i]);
printf("%.10lf",solve(a,n,x)+solve(b,m,y));
}