专栏文章
题解:P12736 [POI 2016 R2] 圣诞灯链 Christmas chain
P12736题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mioygzep
- 此快照首次捕获于
- 2025/12/03 03:12 3 个月前
- 此快照最后确认于
- 2025/12/03 03:12 3 个月前
不用动脑子的简单数据结构做法,思考十分钟代码四十分钟就一发过了。但是时间复杂度稍劣。
题目的条件可以被拆成若干个两个位置相等的条件,暴力拆出来大概是 的,难以接受。考虑并查集维护的过程,你实际是在维护连通性,其有效合并其实只有 次,暴力做法把多数时间花在了找可以合并的位置上,十分不优。以下称位置/数的颜色是其所属集合。
于是我们有一个基本的 idea:我们要让每次合并的尝试都是有效的,即要保证合并的两个位置的颜色不同。
我们维护位置的颜色序列。操作范围是区间,这就很简单,使用哈希就可以简单二分出两个区间第一个颜色不同的位置。使用树状数组可以简单维护修改和查询。每次找合并的位置,时间是 的。
但是你遇到一个问题:你的区间哈希需要有即时性,也就是你每次合并完集合之后需要把修改的颜色同步回序列上。使用启发式合并即可——启发式合并可以让“修改某个位置的颜色”的操作次数做到 。使用上文的树状数组支持修改哈希即可。
最终时间复杂度是 的,常数很小,而且数据貌似没把它卡满,比某些写法稍劣的倍增做法跑得快很多。值得注意的是,尽管这道题没有卡单哈希,我仍然建议使用双哈希,代码如下:
CPP// 注意他开了 4.5s 1G.
// 该思路使用 10min 完成思考。 40min coding.
// 不知道能不能过啊 而且我也不知道 hash 这个问题误报概率有多大。可能写得差不多了得加个双 hash
// feed back: 竟然过了!最慢点跑了 780ms 左右,也是独立切上紫题了。看看加个快读什么的跑得有多快吧。
// 这个空间不像是卡满的样子啊,不过想必确实有点难卡满吧。
#include<bits/stdc++.h>
using namespace std;
typedef vector<int> VI;
typedef long long LL;
const int N=5e5+10;
const int bas=131,mod=998244353;
inline int& addmod(int &x){ return x>=mod?x-=mod:x; }
inline int Mod(int x){return x>=mod?x-mod:x;}
struct IO{
char buf[1<<21],*p1,*p2;
#define gc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read(){
register int x=0;
register char c=gc();
while(c<'0' || c>'9') c=gc();
while(c<='9' && c>='0') x=(x<<3)+(x<<1)+c-'0',c=gc();
return x;
}
}io;
int n,m,pw[N];
struct BIT{
int t[N];
#define lowbit(x) (x&(-x))
inline void update(int x,int v){
v=1ll*v*pw[x]%mod;
for(int i=x;i<=n;i+=lowbit(i))
addmod(t[i]+=v);
}
inline int query(int x){
int ret=0;
for(int i=x;i;i-=lowbit(i))
addmod(ret+=t[i]);
return ret;
}
inline int qrange(int l,int r){
return Mod(query(r)+mod-query(l-1));
}
}hsh;
inline bool comp(int l1,int r1,int l2,int r2){
return 1ll*hsh.qrange(l1,r1)*pw[l2-l1]%mod==hsh.qrange(l2,r2);
}
struct DSU{ // 直接启发式合并
int bel[N],tab[N];
vector<int> e[N];
inline void chg(int u,int fr){
hsh.update(u,Mod(fr+mod-bel[u]));
e[bel[u]=fr].push_back(u);
}
inline void init(){ for(int i=1;i<=n;i++) chg(i,i); }
inline void merge(int u,int v){
assert(bel[u]!=bel[v]); // 应该是不会有问题。
if(bel[u]!=bel[v]){ // 这个地方写法其实有很多,所以 hash 还是很难卡的。
u=bel[u],v=bel[v];
if(e[u].size()<e[v].size())
swap(u,v);
for(int i:e[v])
chg(i,u);
}
}
inline int count(){
int ans=0;
for(int i=1;i<=n;i++)
if(!(tab[bel[i]]++))
++ans;
return ans;
}
}dsu;
void init(){
pw[0]=1;
for(int i=1;i<=n;i++)
pw[i]=1ll*bas*pw[i-1]%mod;
dsu.init();
}
inline void update(int a,int b,int k){
while(!comp(a,a+k-1,b,b+k-1)){
int l=0,r=k-1,mid;
while(l<r){
mid=(l+r)>>1;
if(!comp(a,a+mid,b,b+mid))
r=mid;
else
l=mid+1;
}
dsu.merge(a+l,b+l);
}
}
int main(){
n=io.read(),m=io.read(),init();
for(int i=1,a,b,k;i<=m;i++){
a=io.read(),b=io.read(),k=io.read();
if(a>b) swap(a,b);
update(a,b,k);
}
printf("%d\n",dsu.count());
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...