20210622模拟赛
早上迟到了,然后手机被没收了(痛苦面具
整个上午考试都好困,看教练不在就睡了半程2333
要结束的时候通过某奇怪的方式调出了写挂的bug
就是最后还是把代码拖到windows下来调了(我错了,我不敢了。
三道题分别是 dp,数据结构加速分治,子集(超集)卷。
T1:是个sb题 理解错题意然后弃了。
T2:目测 4k 代码,写了暴力溜了。
T3:切了。然而我时间多了个log极限卡常居然能过,教练机子nb
据说是三道原题,不知道(
今天题解写不完了,但是明天应该没有模拟赛(只要教练不抽风),所以咕到明天叭,先放代码。
updata 6.23 补完坑了!但是事实上今天还是有模拟赛,新的坑又增加了 55555。
A
倒过来做,不是很难的dp,略过。
B
区间问题,考虑分治。
对于左区间 [ L , M i d ] [L,Mid] [L,Mid] 我们维护它每个后缀的领域(圆),对于右区间 [ M i d + 1 , R ] [Mid+1,R] [Mid+1,R] 维护每个前缀的领域。领域我们用 C ( u , r ) C(u,r) C(u,r) 来表示, u u u 表示直径的中点, r r r 表示半径。 C ( u i , r i ) , i ∈ [ L , M i d ] C(u_i,r_i),i\in[L,Mid] C(ui,ri),i∈[L,Mid] 表示 [ i , M i d ] [i,Mid] [i,Mid] 中的点构成的领域。 C ( u j , r j ) , j ∈ [ M i d + 1 , R ] C(u_j,r_j),j\in[Mid+1,R] C(uj,rj),j∈[Mid+1,R] 表示 [ M i d + 1 , j ] [Mid+1,j] [Mid+1,j] 中的点构成的领域。
考虑算左区间的点 i i i 的贡献,即 l = i , r ∈ [ M i d + 1 , R ] l=i,r\in[Mid+1,R] l=i,r∈[Mid+1,R] 的直径的和。
那么右边序列一定存在一个前缀 [ M i d + 1 , a ] [Mid+1,a] [Mid+1,a] 满足前缀中的所有领域都包含了 C ( u i , r i ) C(u_i,r_i) C(ui,ri) ,此时直径为 r i × 2 r_i\times 2 ri×2 。当然前缀可以为空。
同时也存在一个后缀 [ b , R ] [b,R] [b,R] 满足 C ( u i , r i ) C(u_i,r_i) C(ui,ri) 包含后缀中的所有领域,此时直径的和为 ∑ j ∈ [ b , R ] r j × 2 \sum_{j\in[b,R]} r_j\times 2 ∑j∈[b,R]rj×2 。
除这两个前后缀外,中间的领域要么和 C ( u i , r i ) C(u_i,r_i) C(ui,ri) 相离,要么相交。这样的两个领域合并起来的新的领域的直径为 两个中心点的距离+两个领域的半径。所以只需要求出点 u i u_i ui 到中间的领域的中心点的距离和即可。
这个用点分治可以解决,提前把点分树建出来,把右边需要求距离的中心点都挂上去,用完一个删一个,询问和修改都是 O ( log n ) O(\log n) O(logn) 的 。
到现在我们只算了单个点 i i i 的贡献,但是显然 a a a 和 b b b 随着左边领域的扩大也是单调不减的,所以挨着从 M i d Mid Mid 算到 L L L 就可以了。
分治 O ( n log n ) O(n\log n) O(nlogn) ,单次询问或修改点分树 O ( log n ) O(\log n) O(logn) ,总时间复杂度 O ( n log 2 n ) O(n\log^2 n) O(nlog2n) 。
建议用树剖+树状数组代替点分,时间会多一个 log \log log ,但是实测速度吊打点分(就很难受。
#include <bits/stdc++.h>
#define N 200005
using namespace std;
typedef long long ll;
void read(int &x){
x=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
int n,nn;
ll ans;
vector<int> ed[N];
int fa[N][20],lst[N][20],dep[N];
bool tag[N];
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--)
if(dep[lst[x][i]]>=dep[y]) x=lst[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--)
if(lst[x][i]!=lst[y][i]) x=lst[x][i],y=lst[y][i];
return lst[x][0];
}
int dis(int x,int y){
int res=0;
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--)
if(dep[lst[x][i]]>=dep[y]) res+=(1<<i),x=lst[x][i];
if(x==y) return res;
for(int i=19;i>=0;i--)
if(lst[x][i]!=lst[y][i]) res+=(1<<i+1),x=lst[x][i],y=lst[y][i];
return res+2;
}
int get_mid(int x,int y,int lcaa,int r){
// cout<<x<<' '<<y<<' '<<r<<' '<<endl;
int dist=dep[x]+dep[y]-(dep[lcaa]<<1);
if(dep[x]-r<dep[lcaa]) swap(x,y),r=dist-r;
int now=0;
for(int i=19;i>=0;i--) if((now+(1<<i))<=r) now+=(1<<i),x=lst[x][i];
// cout<<x<<endl;
return x;
}
void init(int x){
// cout<<lst[x][0]<<' '<<x<<endl;
dep[x]=dep[lst[x][0]]+1;
for(int i=1;i<=19;i++) lst[x][i]=lst[lst[x][i-1]][i-1];
for(int y : ed[x]) if(y!=lst[x][0]) lst[y][0]=x,init(y);
}
int siz[N],maxn[N],far[N][19];
void get_root(int &rt,int sum,int x,int faa){
siz[x]=1; maxn[x]=0;
for(int y : ed[x])
if(y!=faa&&!tag[y])
get_root(rt,sum,y,x),siz[x]+=siz[y],maxn[x]=max(maxn[x],siz[y]);
maxn[x]=max(maxn[x],sum-siz[x]);
if(!rt||maxn[x]<maxn[rt]) rt=x;
}
void bfs(int rt,int x,int faa,int ty){
siz[x]=1;
fa[x][ty]=rt;
for(int y : ed[x])
if(y!=faa&&!tag[y])
far[y][ty]=far[x][ty]+1,bfs(rt,y,x,ty),siz[x]+=siz[y];
}
void build(int x,int sum,int ty){
int rt=0;
get_root(rt,sum,x,0);
bfs(rt,rt,0,ty);
tag[rt]=1;
for(int y : ed[rt]) if(!tag[y]) build(y,siz[y],ty+1);
}
struct edge{
int mid,r;
}cc[N];
bool chk(int x,edge y){ return dis(y.mid,x)<=y.r; }
void merge(edge &x,edge y){
int lcaa=lca(x.mid,y.mid),len=dep[x.mid]+dep[y.mid]-dep[lcaa]*2;
if(len<=y.r){ x=y; return; }
x.r=(y.r+len)/2; x.mid=get_mid(x.mid,y.mid,lcaa,x.r);
}
ll sum_dis[N],cnt[N];
ll NOW;
void add(edge x){
NOW+=x.r; int u=x.mid;
for(int i=1;fa[u][i];i++) sum_dis[fa[u][i]]+=far[u][i]-far[u][i-1],cnt[fa[u][i]]++;
}
void del(edge x){
NOW-=x.r; int u=x.mid;
for(int i=1;fa[u][i];i++) sum_dis[fa[u][i]]-=far[u][i]-far[u][i-1],cnt[fa[u][i]]--;
}
ll ask(int x){
ll res=0;
for(int i=1;fa[x][i];i++) res+=sum_dis[fa[x][i]]+cnt[fa[x][i]]*(far[x][i]-far[x][i-1]);
return res;
}
void solve(int l,int r,bool way){
if(l==r){ cc[l]={l,0}; return; }
int mid=(l+r)>>1;
solve(l,mid,0),solve(mid+1,r,1);
int a=mid,b=mid+1; ll sum=0;
while(b<=r&&!chk(mid,cc[b])) add(cc[b]),b++;
for(int i=b;i<=r;i++) sum+=cc[i].r<<1;
for(int i=mid;i>=l;i--){
while(b<=r&&!chk(i,cc[b])) sum-=(cc[b].r<<1),add(cc[b]),b++;
while(a<r&&a+1<b&&chk(a+1,cc[i])) a++,del(cc[a]);
ans+=1ll*(a-mid)*cc[i].r*2+sum+ask(cc[i].mid)+NOW+1ll*cc[i].r*(b-a-1);
}
for(int i=a+1;i<b;i++) del(cc[i]);
if(way==0){
cc[r]={r,0};
for(int i=r-1;i>=l;i--) cc[i]={i,0},merge(cc[i],cc[i+1]);
}
else{
cc[l]={l,0};
for(int i=l+1;i<=r;i++) cc[i]={i,0},merge(cc[i],cc[i-1]);
}
}
int main(){
// freopen("image26.in","r",stdin);
read(n);
int u,v;
for(int i=1;i<n;i++){
read(u),read(v);
ed[u].push_back(i+n),ed[v].push_back(i+n);
ed[i+n].push_back(u),ed[i+n].push_back(v);
}
nn=n+n-1;
init(1);
build(1,nn,1);
solve(1,n,0);
cout<<ans/2;
}
C
由于在第一个串直接交换一个区间比较棘手,考虑如何将一个区间的操作差分,这显然是不能在一个串上 实现的,因此我们可以考虑在第二个串上做文章(方便起见,下面使用 S S S, T T T 分别表示第一个串和第二个串)。
直接用前缀操作差分,观察可知对于 S S S 做一遍操作 l → r l → r l→r 的答案和对 T T T 做一遍 r → l r → l r→l 的答案是相同的,所 以进一步可以推出 对 S S S 操作 l → r l → r l→r 的答案 等于 对 S S S 操作 l − 1 → 1 l − 1 → 1 l−1→1 再对于 t t t 操作 r → 1 r → 1 r→1。
这样子的话我们可以 O ( n k ) O(nk) O(nk) 预处理出 f s f_s fs 表示 S S S 某个状态 s s s 出现的最早时间, g s g_s gs 表示 T T T 某个状态 s s s 出现 的最晚时间(注意是从该时刻反向操作的状态 s s s)。
事实上,最大化两个串相同的位数等价于最大化两个串均为 1 1 1 的位数。
设 S S S 有 a a a 位为 1 1 1, T T T 有 b b b 位为 1 1 1,公共为 1 1 1 的位数有 c c c 位,答案就是 c + ( k − c − ( a − c ) − ( b − c ) ) = k + 2 c − a − b c+(k−c−(a−c)−(b−c)) = k+2c−a−b c+(k−c−(a−c)−(b−c))=k+2c−a−b, 最大化 c c c 即可。 考虑直接枚举交集 s s s,然后再使用上面预处理的 f f f, g g g 判断是否存在 g t − f t ≥ M , s ⊆ t g_t − f_t ≥ M, s ⊆ t gt−ft≥M,s⊆t,如果直接枚举子集 复杂度 O ( k 3 k ) O(k3^k ) O(k3k),直接高维后缀 m i n / m a x min/max min/max 优化复杂度就是 O ( k 2 k ) O(k2^k) O(k2k) 了。 总复杂度 O ( n k + k 2 k ) O(nk + k2^k) O(nk+k2k)。
下面的程序时间是多乘了一个 k k k 的,但是它过了。(不管
#include <bits/stdc++.h>
#define N 1100006
using namespace std;
int ti[N][11],a[N],b[N],o;
int n,m,k,cc[22],dd[22],SS,TT,maxn;
char S[22],T[22];
vector<int> pos_s,pos_t;
void read(int &x){
x=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
}
void init(int now,int t,int num){
if(!num||ti[now][num]!=o) return;
ti[now][num]=t;
for(int i=0;i<k;i++) if(now&(1<<i)) init(now^(1<<i),t,num-1);
}
int ansl=1,ansr,ans=0;
int main(){
// freopen("option3.in","r",stdin);
// freopen("option.out","w",stdout);
memset(ti,127/3,sizeof(ti)); o=ti[0][0];
read(n),read(m),read(k); maxn=(1<<k)-1; ansr=m;
scanf("%s",S),scanf("%s",T);
int now=0;
for(int i=0;i<k;i++){
now|=((S[i]=='1')<<i);
cc[i]=dd[i]=i;
if(S[i]=='1') pos_s.push_back(i),SS++;
if(T[i]=='1') pos_t.push_back(i),TT++;
}
if(SS>10){
now=SS=TT=0;
pos_s.clear(),pos_t.clear();
for(int i=0;i<k;i++){
now|=((S[i]=='0')<<i);
cc[i]=dd[i]=i;
if(S[i]=='0') pos_s.push_back(i),SS++;
if(T[i]=='0') pos_t.push_back(i),TT++;
}
}
init(now,0,SS);
int u,v;
for(int i=1;i<=n;i++){
read(a[i]),read(b[i]); a[i]--,b[i]--;
swap(cc[a[i]],cc[b[i]]);
now=0;
for(int j : pos_s) now|=(1<<cc[j]);
init(now,i,SS);
}
int cnt=0,x;
for(int i=0;i<=maxn;i++){
cnt=0;
for(int l=0;l<k;l++) cnt+=(i&(1<<l))?1:0;
if(cnt>=TT) continue;
for(int j=1;j<=SS;j++){
if(ti[i][j]==o) break;
for(int l=0;l<k;l++){
if(!((1<<l)&i))
ti[(1<<l)|i][j]=min(ti[(1<<l)|i][j],ti[i][j]);
}
}
}
for(int i=1;i<=n;i++){
swap(dd[a[i]],dd[b[i]]);
now=0;
for(int j : pos_t) now|=(1<<dd[j]);
for(int j=ans+1;j<=SS;j++){
if(ti[now][j]<=i-m) ans=j,ansl=ti[now][j]+1,ansr=i;
}
}
cout<<k-(SS-ans)-(TT-ans)<<'\n';
cout<<ansl<<' '<<ansr<<'\n';
}