【枚举优化/离散化/并查集】染色问题
2012-08-15 21:59
507 查看
2.染色问题(color.pas/c/cpp)
【题目描述】
平面上有n个珠子排成一排,每个珠子初始颜色为0,你要对他们进行m次染色,每次你选定l和r,然后把[l,r]之间的珠子染成编号c的颜色,每个珠子的最终颜色为它曾经染过的编号最大的颜色,请你写个程序统计每个珠子最终的颜色。
【输入格式】
第一行两个数n,m,表示珠子个数和染色的次数
接下来m行,每行三个数l,r,c如题意所示
【输出格式】
由于数据较大,为了减少输出所用的不必要的时间,请采取以下方法输出:
假如a[i]为第i个珠子的最终颜色
ans := 0;
for i := 1 to n do ans := (ans * 1200007 + a[i]) mod 999911659;
writeln(ans);
注意用int64保存相关变量,防止运算过程中越界
【样例输入】
3 2
1 2 1
2 2 2
【样例输出】
146411103
【数据范围】
30% n,m<=5000
50% n,m <= 10000
80% n,m <= 500000
100% n <= 1000000, m <= 2000000
【时间限制】
2s
这道题很好,方法有四:(线段树的方法就算了,不在noip要求范围内)
方法一:AC。O(n*m)
这道题我还做得不错,AC掉了。
但是,主要在于我用的是朴素,他们都弱爆了,搞半天都超时。。
主要思路是枚举i从1到n,再枚举编号最小的左右边界能覆盖到i的j区间。
用了一个预处理优化,lmin[i]记录左边界能覆盖到i的编号最小的区间。
rmin[i]记录右边界能覆盖到i得编号最小区间。lmax和rmax同理。
每次枚举就从max(lmin[i],rmin[i])到min(lmax[i],rmax[i])。这样就AC了。。= =、比较弱智、、但是有效。
方法二:TLE O(nlgn)
离散化
这个方法是Laury提出来的,我帮他实现了,理论复杂度是能够过的,但是由于其中的一个缺陷而速度大降(由gprof测试得到),也就是从堆中删除已经使用过的颜色太慢,暂时没有找到更好的解决办法。
主要思路是离散化+堆。堆按照颜色编号排序。
离散化因为范围过大,我用链表实现,把区间转化为左闭右开,然后在区间的位置处插入区间边界的信息(一般来说右边界优先(但是相反的要后加入,因为链表的特殊插入方式))。
从1到n枚举,遇到左区间,就将颜色入堆(注意不能用栈,例如这种情况([)],如果用栈的话,并不能括号匹配)
遇到右区间,就将该区间的编号标为已访问。
不管有没有遇到边界,都从堆顶不断取出区间的编号,直到这个区间没有被访问过。如果堆为空,则颜色为0,否则颜色为堆顶对应的区间的颜色。
血的教训,堆是按照颜色排序的。。因此。。如果说颜色重复。。!!所以我用了set就死了。。只能用Mulitiset!!
方法三:AC。并查集。O(nm),最优O(n)。
这是最快的方法。
每次涂了一个点,就把它合并到区间的右边界(开区间),涂下一个点时,就更新为getroot(u+1)。
需要注意的是,初始化u = getroot(getint())而不是 u = getint();因为可能一开始就在涂过的地方了。
if (getroot(1) == n+1) break这是一个行之有效的优化。
【题目描述】
平面上有n个珠子排成一排,每个珠子初始颜色为0,你要对他们进行m次染色,每次你选定l和r,然后把[l,r]之间的珠子染成编号c的颜色,每个珠子的最终颜色为它曾经染过的编号最大的颜色,请你写个程序统计每个珠子最终的颜色。
【输入格式】
第一行两个数n,m,表示珠子个数和染色的次数
接下来m行,每行三个数l,r,c如题意所示
【输出格式】
由于数据较大,为了减少输出所用的不必要的时间,请采取以下方法输出:
假如a[i]为第i个珠子的最终颜色
ans := 0;
for i := 1 to n do ans := (ans * 1200007 + a[i]) mod 999911659;
writeln(ans);
注意用int64保存相关变量,防止运算过程中越界
【样例输入】
3 2
1 2 1
2 2 2
【样例输出】
146411103
【数据范围】
30% n,m<=5000
50% n,m <= 10000
80% n,m <= 500000
100% n <= 1000000, m <= 2000000
【时间限制】
2s
这道题很好,方法有四:(线段树的方法就算了,不在noip要求范围内)
方法一:AC。O(n*m)
这道题我还做得不错,AC掉了。
但是,主要在于我用的是朴素,他们都弱爆了,搞半天都超时。。
主要思路是枚举i从1到n,再枚举编号最小的左右边界能覆盖到i的j区间。
用了一个预处理优化,lmin[i]记录左边界能覆盖到i的编号最小的区间。
rmin[i]记录右边界能覆盖到i得编号最小区间。lmax和rmax同理。
每次枚举就从max(lmin[i],rmin[i])到min(lmax[i],rmax[i])。这样就AC了。。= =、比较弱智、、但是有效。
#include <cstdio> #include <algorithm> using std::max; using std::min; struct JEW { long l; long r; long c; bool operator<(const JEW& j2)const { return c < j2.c; } }; JEW jew[2000010]; long lmax[1000010]; long lmin[1000010]; long rmax[1000010]; long rmin[1000010]; long getint() { long rs=0;bool sgn=1;char tmp; do tmp = getchar(); while (!isdigit(tmp)&&tmp-'-'); if (tmp=='-'){tmp=getchar();sgn=0;} do rs=(rs<<1)+(rs<<3)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } int main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); long n = getint(); long m = getint(); for (long i=1;i<m+1;i++) { jew[i].l = getint(); jew[i].r = getint(); jew[i].c = getint(); } std::sort(jew+1,jew+m+1); memset(lmin,0x7f,sizeof lmin); memset(rmin,0x7f,sizeof rmin); for (long i=m;i>0;i--) { lmax[jew[i].l] = max(lmax[jew[i].l],i); lmin[jew[i].l] = min(lmin[jew[i].l],i); rmax[jew[i].r] = max(rmax[jew[i].r],i); rmin[jew[i].r] = min(rmin[jew[i].r],i); } for (long i=1;i<n+1;i++) { lmin[i] = min(lmin[i],lmin[i-1]); lmax[i] = max(lmax[i],lmax[i-1]); } for (long i=n;i>0;i--) { rmin[i] = min(rmin[i],rmin[i+1]); rmax[i] = max(rmax[i],rmax[i+1]); } long long ans = 0ll; for (long i=1;i<n+1;i++) { bool ok = false; for (long j=min(lmax[i],rmax[i]);j>=max(lmin[i],rmin[i]);j--) { if (jew[j].l<=i && jew[j].r>=i) { ok = true; ans = (ans*1200007ll+jew[j].c)%999911659ll; break; } } if (!ok) { ans = (ans*1200007ll)%999911659ll; } } printf("%ld",ans); return 0; }
方法二:TLE O(nlgn)
离散化
这个方法是Laury提出来的,我帮他实现了,理论复杂度是能够过的,但是由于其中的一个缺陷而速度大降(由gprof测试得到),也就是从堆中删除已经使用过的颜色太慢,暂时没有找到更好的解决办法。
主要思路是离散化+堆。堆按照颜色编号排序。
离散化因为范围过大,我用链表实现,把区间转化为左闭右开,然后在区间的位置处插入区间边界的信息(一般来说右边界优先(但是相反的要后加入,因为链表的特殊插入方式))。
从1到n枚举,遇到左区间,就将颜色入堆(注意不能用栈,例如这种情况([)],如果用栈的话,并不能括号匹配)
遇到右区间,就将该区间的编号标为已访问。
不管有没有遇到边界,都从堆顶不断取出区间的编号,直到这个区间没有被访问过。如果堆为空,则颜色为0,否则颜色为堆顶对应的区间的颜色。
血的教训,堆是按照颜色排序的。。因此。。如果说颜色重复。。!!所以我用了set就死了。。只能用Mulitiset!!
#include <algorithm> #include <cstdio> #include <string> long l[2000010]; long r[2000010]; long c[2000010]; bool used[2000010]; struct node { long i; node* nxt; node() {} node(long i, node* nxt): i(i), nxt(nxt) {} }; node* head[1000010]; node memory[4000010]; node* ptr = memory; inline void insert(long a,long c) { head[a] = new (ptr++) node(c, head[a]); } inline long getint() { long rs=0;bool sgn=1;char tmp; do tmp = getchar(); while (!isdigit(tmp)&&tmp-'-'); if (tmp=='-'){tmp=getchar();sgn=0;} do rs=(rs<<3)+(rs<<1)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } long size = 0; long heap[8000000]; inline void swap(long a,long b) { std::swap(heap[a], heap[b]); } inline void adjust_down(long l) { while ((l<<=1)<size+1) { if (l<size&&c[heap[l]]<c[heap[l+1]]) l ++; if (c[heap[l]]>c[heap[l>>1]]) swap(l,l>>1); else break; } } inline void adjust_up(long l) { while (l > 1) { if (c[heap[l]]>c[heap[l>>1]]) swap(l,l>>1); else break; l >>= 1; } } inline void push(long a) { heap[++size] = a; adjust_up(size); } inline void pop() { heap[1] = heap[size--]; adjust_down(1); } inline long top() { return heap[1]; } int main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); long n = getint(); long m = getint(); for (long i=1;i<m+1;i++) { l[i] = getint(); r[i] = getint(); c[i] = getint(); insert(r[i]+1,-i); insert(l[i],i); } long long ans = 0; for (long i=1;i<n+1;i++) { for (node* vv=head[i];vv;vv=vv->nxt) { long v = std::abs(vv->i); bool f = vv->i > 0; if (f) push(v);//Modify the compare program else used[v] = true; } while (size>0&&used[top()]) pop(); if (size == 0) ans = (ans*1200007ll)%999911659ll; else ans = (ans*1200007ll+c[top()])%999911659ll; } printf("%ld",ans); return 0; }
方法三:AC。并查集。O(nm),最优O(n)。
这是最快的方法。
每次涂了一个点,就把它合并到区间的右边界(开区间),涂下一个点时,就更新为getroot(u+1)。
需要注意的是,初始化u = getroot(getint())而不是 u = getint();因为可能一开始就在涂过的地方了。
if (getroot(1) == n+1) break这是一个行之有效的优化。
#include <cstdio> #include <algorithm> using std::sort; long fa[1000010]; long color[1000010]; long getroot(long u) { if (fa[u] == u) return u; return fa[u] = getroot(fa[u]); } void merge(long a,long b) { fa[getroot(a)] = getroot(b); } struct Rag { long l; long r; long c; bool operator<(const Rag& rag2)const { return c > rag2.c; } }range[2000010]; long getint() { long rs=0;bool sgn=1;char tmp; do tmp=getchar(); while (!isdigit(tmp)&&tmp-'-'); if (tmp=='-'){tmp=getchar();sgn=1;} do rs=(rs<<3)+(rs<<1)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } int main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); long n = getint(); long m = getint(); for (long i=1;i<n+1;i++) fa[i] = i; fa[n+1] = n+1; for (long l=1;l<m+1;l++) { range[l].l = getint(); range[l].r = getint(); range[l].c = getint(); } sort(range+1,range+1+m); for (long l=1;l<m+1;l++) { long u = getroot(range[l].l); while (u < range[l].r+1) { color[u] = range[l].c; merge(u,range[l].r+1); u = getroot(u+1); } if (getroot(1) == n+1) break; } long long ans = 0; for (long i=1;i<n+1;i++) { ans = (ans * 1200007 + color[i]) % 999911659; } long out = ans; printf("%ld",out); return 0; }
相关文章推荐
- acdream 1725 哗啦啦的小彭玉染色问题 离散化并查集
- bzoj 1171 并查集优化顺序枚举 | 线段树套单调队列
- 枚举百鸡问题的算法优化
- [并查集]染色问题
- HDU 1856 More is better(并查集路径压缩+剪枝优化+暴力枚举)
- POJ 2528 Mayor's posters(线段树染色问题+离散化)
- 一个并查集问题的优化(CDOJ 203)
- hdu 4056 并查集处理线段树染色问题
- POJ 2528 Mayor's posters (线段树,染色问题,离散化要注意)
- 优化枚举问题
- 深度分析Java的枚举类型—-枚举的线程安全性及序列化问题
- HDU 5009 Paint Pearls(西安网络赛C题) dp+离散化+优化
- VB代码优化和资源初始化和释放问题
- android优化问题
- 关于SSH的性能优化问题
- unity树优化 speedtree lod问题
- spark内核揭秘-14-Spark性能优化的10大问题及其解决方案
- nyoj 一笔画问题(并查集,欧拉路)
- 并查集问题:简单java实现
- 哗啦啦族的01背包问题(折半枚举)