Record
- 8:06 会了 T1。特殊性质立大功。
- 8:22 过掉 T1 大洋里。开 T2。
- 8:30 没有任何思路。
- 9:16 思考 T2 思考了一个小时但还是没有任何头绪。放弃 T2。听说 T3 比 T2 可做。
- 10:23 写完 T3 了。直接过掉大洋里。
- 11:36 拍了拍 T3,发现了一个细节错误。这下应该没错了。
- (忘了几点)开 T4。打了个表然后啥也看不出来。遂放弃。
最后是 100+18+100+0。
T1 限速
题意
给定一张图,求这张图的生成树中权值最小的一个。
定义生成树的权值为:
- 若树中最大权值的边 \(w_{\max}\le k\),则权值为 \(k-w_{\max}\);
- 否则权值为 \(\sum_{w_i\ge k}|w_i-k|\)。
赛时
对于两个特殊性质,也就是 \(w_i\le k\) 和 \(w_i\ge k\) 而言,都是简单的。
所以考虑把两种情况合起来考虑,所以做完了。
题解
首先将原图所有边权 \(\ge k\) 的边去掉,跑一次 Kruskal。
如果图已经连通了,那么我们再把小于等于 \(k\) 的 \(w_{\max}\) 这条边加上去,那么答案就是 \(k-w_{\max}\)。
另一种情况,我们可以选择 \(\gt k\) 的最小边,让这棵树计算答案变成第二种。所以这种情况的答案就是两种取 \(\min\)。
如果图不连通,那么我们再在刚才的图的基础上加上 \(\gt k\) 的边再跑一次 Kruskal。然后答案计算就很显然了。
哦对了,并查集用了一个黑科技。想了解的可以去看这篇文章。或者是搬运到ZYZOJ上的版本。
时间复杂度 \(O(m\log m+m\alpha(n))\)。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;int n,m;
long long k;struct node{int x,y;long long v;bool operator<(const node&_Q)const{return v<_Q.v;}
};
vector<node> v_le,v_gt;int fa[200010],siz[200010];
void pathzip(int&x){while(fa[x]!=x)x=fa[x]=fa[fa[x]];} // 黑科技int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>m>>k;for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;for(int i=1;i<=m;i++){int x,y;cin>>x>>y;long long v;cin>>v;if(v<=k) v_le.push_back({x,y,v});else v_gt.push_back({x,y,v});}int addcnt=0;long long v_le_max=-infll;for(auto pr:v_le){int x=pr.x;int y=pr.y;pathzip(x);pathzip(y);if(x!=y){if(siz[x]>siz[y]) swap(x,y);fa[x]=y,siz[y]+=siz[x];addcnt++;}v_le_max=max(v_le_max,pr.v);}sort(v_gt.begin(),v_gt.end());if(addcnt==n-1){long long ans=0;if(v_gt.size()==0) ans=k-v_le_max;else ans=min(k-v_le_max,v_gt[0].v-k);cout<<ans<<"\n";}else{long long ans=0;for(auto pr:v_gt){int x=pr.x;int y=pr.y;pathzip(x);pathzip(y);if(x!=y){if(siz[x]>siz[y]) swap(x,y);fa[x]=y,siz[y]+=siz[x];ans+=pr.v-k;}}cout<<ans<<"\n";}# ifndef ONLINE_JUDGEcerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";# endifreturn 0;
}
T3 单峰数列
题意
维护一个序列,支持以下操作:
- 操作 \(1\):将在区间 \([l,r]\) 中的数加上 \(x\)。
- 操作 \(2\):查询区间 \([l,r]\) 的所有数是否全都相等。
- 操作 \(3\):查询区间 \([l,r]\) 是否严格单调递增。
- 操作 \(4\):查询区间 \([l,r]\) 是否严格单调递减。
- 操作 \(5\):查询区间 \([l,r]\) 是否呈单峰,即存在一个位置 \(l\lt p\lt r\),\([l,p]\) 严格单调递增,\([p,r]\) 严格单调递减。
赛时
T3 比 T2 可做是真的。
题解
序列问题,一眼神秘数据结构。
但是线段树似乎没办法直接维护这东西。
所以考虑分块。
其实存在非常天才的线段树维护差分的做法,但是我赛时没想出来,所以这里介绍我的神秘分块做法。
给每个块三个标签,分别代表块内所有元素满足全部相等、单调递增、单调递减。
然后带上懒标记就解决了前四个操作。
考虑操作 \(5\),不难注意到,位置 \(p\) 是且仅能是区间内的最大值。
所以再多维护一个区间最大值和对应的位置,则操作 \(5\) 成立的条件为 \([l,p]\) 满足操作 \(3\)、\([p,r]\) 满足操作 \(4\)。
因为懒了所以查询区间最大值直接上了线段树。
时间复杂度 \(O(q\log n+q\sqrt{n})\)。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;class segmenttree{private:struct node{int l,r;pair<long long,int> val_max;long long lz_add;}t[400010];public:void pushdown(int p){if(!t[p].lz_add) return;t[p<<1].lz_add+=t[p].lz_add;t[p<<1|1].lz_add+=t[p].lz_add;t[p<<1].val_max.first+=t[p].lz_add;t[p<<1|1].val_max.first+=t[p].lz_add;t[p].lz_add=0;}void pushup(int p){t[p].val_max=max(t[p<<1].val_max,t[p<<1|1].val_max);}void build(int l,int r,long long *a,int p=1){t[p]={l,r,{-infll,0},0};if(l==r) t[p].val_max={a[l],l};else{int mid=(l+r)>>1;build(l,mid,a,p<<1);build(mid+1,r,a,p<<1|1);pushup(p);}}void modify(int l,int r,long long v,int p=1){if(l<=t[p].l&&t[p].r<=r){t[p].lz_add+=v;t[p].val_max.first+=v;}else{pushdown(p);int mid=(t[p].l+t[p].r)>>1;if(l<=mid) modify(l,r,v,p<<1);if(r>mid) modify(l,r,v,p<<1|1);pushup(p);}}pair<long long,int> query(int l,int r,int p=1){if(l<=t[p].l&&t[p].r<=r) return t[p].val_max;pushdown(p);int mid=(t[p].l+t[p].r)>>1;pair<long long,int> res={-infll,0};if(l<=mid) res=max(res,query(l,r,p<<1));if(r>mid) res=max(res,query(l,r,p<<1|1));return res;}
};
segmenttree segt;int n,q;
long long a[100010];int block_length,belong[100010],block_first[320];long long block_lz_add[320];
int block_tag[320];void pushdown(int x){if(!block_lz_add[x]) return;for(int i=block_first[x];i<block_first[x+1];i++) a[i]+=block_lz_add[x];block_lz_add[x]=0;
}void rebuild(int x){bool is_inc=true;bool is_dec=true;bool is_allsame=true;for(int p=block_first[x]+1;p<block_first[x+1];p++){is_inc&=(a[p-1]<a[p]);is_dec&=(a[p-1]>a[p]);is_allsame&=(a[p-1]==a[p]);}if(is_inc) block_tag[x]=1;else if(is_dec) block_tag[x]=2;else if(is_allsame) block_tag[x]=3;else block_tag[x]=0;
}void operation1(int l,int r,long long x){if(belong[l]==belong[r]){for(int i=l;i<=r;i++) a[i]+=x;pushdown(belong[l]);rebuild(belong[l]);}else{for(int i=l;i<block_first[belong[l]+1];i++) a[i]+=x;for(int i=belong[l]+1;i<belong[r];i++) block_lz_add[i]+=x;for(int i=block_first[belong[r]];i<=r;i++) a[i]+=x;pushdown(belong[l]);pushdown(belong[r]);rebuild(belong[l]);rebuild(belong[r]);}
}bool operation2(int l,int r){bool is_allsame=true;if(belong[l]==belong[r]){pushdown(belong[l]);rebuild(belong[l]);for(int i=l+1;i<=r;i++) is_allsame&=(a[i-1]==a[i]);}else{pushdown(belong[l]);pushdown(belong[r]);rebuild(belong[l]);rebuild(belong[r]);long long same_value=a[l];for(int i=l;i<block_first[belong[l]+1];i++) is_allsame&=(a[i]==same_value);for(int i=belong[l]+1;i<belong[r];i++) is_allsame&=(block_tag[i]==3&&block_lz_add[i]+a[block_first[i]]==same_value);for(int i=block_first[belong[r]];i<=r;i++) is_allsame&=(a[i]==same_value);}return is_allsame;
}bool operation3(int l,int r){bool is_inc=true;if(belong[l]==belong[r]){pushdown(belong[l]);rebuild(belong[l]);for(int i=l+1;i<=r;i++) is_inc&=(a[i-1]<a[i]);}else{pushdown(belong[l]);pushdown(belong[r]);rebuild(belong[l]);rebuild(belong[r]);for(int i=l+1;i<block_first[belong[l]+1];i++) is_inc&=(a[i-1]<a[i]);for(int i=belong[l]+1;i<belong[r];i++) is_inc&=(block_tag[i]==1&&block_lz_add[i-1]+a[block_first[i]-1]<block_lz_add[i]+a[block_first[i]]);is_inc&=(block_lz_add[belong[r]-1]+a[block_first[belong[r]]-1]<a[block_first[belong[r]]]);for(int i=block_first[belong[r]]+1;i<=r;i++) is_inc&=(a[i-1]<a[i]);}return is_inc;
}bool operation4(int l,int r){bool is_dec=true;if(belong[l]==belong[r]){pushdown(belong[l]);rebuild(belong[l]);for(int i=l+1;i<=r;i++) is_dec&=(a[i-1]>a[i]);}else{pushdown(belong[l]);pushdown(belong[r]);rebuild(belong[l]);rebuild(belong[r]);for(int i=l+1;i<block_first[belong[l]+1];i++) is_dec&=(a[i-1]>a[i]);for(int i=belong[l]+1;i<belong[r];i++) is_dec&=(block_tag[i]==2&&block_lz_add[i-1]+a[block_first[i]-1]>block_lz_add[i]+a[block_first[i]]);is_dec&=(block_lz_add[belong[r]-1]+a[block_first[belong[r]]-1]>a[block_first[belong[r]]]);for(int i=block_first[belong[r]]+1;i<=r;i++) is_dec&=(a[i-1]>a[i]);}return is_dec;
}bool operation5(int l,int r){pair<long long,int> wmax=segt.query(l,r);if(wmax.second==l||wmax.second==r) return false;return operation3(l,wmax.second)&&operation4(wmax.second,r);
}int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];block_length=max((int)sqrt(n),1);for(int i=1;i<=n;i++){belong[i]=(i-1)/block_length+1;if(!block_first[belong[i]]) block_first[belong[i]]=i;}block_first[belong[n]+1]=n+1;for(int i=1;i<=belong[n];i++){bool is_inc=true;bool is_dec=true;bool is_allsame=true;for(int p=block_first[i]+1;p<block_first[i+1];p++){is_inc&=(a[p-1]<a[p]);is_dec&=(a[p-1]>a[p]);is_allsame&=(a[p-1]==a[p]);}if(is_inc) block_tag[i]=1;else if(is_dec) block_tag[i]=2;else if(is_allsame) block_tag[i]=3;else block_tag[i]=0;}segt.build(1,n,a);cin>>q;while(q--){int op,l,r;cin>>op>>l>>r;if(op==1){long long x;cin>>x;operation1(l,r,x);segt.modify(l,r,x);}else if(op==2) cout<<operation2(l,r)<<"\n";else if(op==3) cout<<operation3(l,r)<<"\n";else if(op==4) cout<<operation4(l,r)<<"\n";else if(op==5) cout<<operation5(l,r)<<"\n";}# ifndef ONLINE_JUDGEcerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";# endifreturn 0;
}
总结
T2 是什么啥比 Ad-hoc。。我为什么看不懂题解在说啥。。
T4 不会。