http://poj.org/problem?id=3697
题意是问在N个点的完全图(N<=10,000)中删掉M(M<=1,000,000)条边? q有多少个点与顶?q?点~号?开?.
暴力BFS+HASH+各种WS优化的方法很? 但是出题人原意应该是O(M)的做法吧... 下面是我YY出来的O(M)的做?
只考虑qM条待删边, 能得C么信息呢? 可以先从?入手. 扫一遍与1L的点, 那么剩下没被扫到的点肯定?是连通的. 而被扫到的点?有可??不连? 所以就把那些肯定与1q通的点做标记. 从这些确定连通的点中任选一个u, 再扫描它的所有邻接点. q之? 如果一个点一p扫了2? 那么它才"有可??不连? 其它于2ơ的点肯定与1q? 因ؓ它或者与1q? 或者与uq? 而u是已知与1q通的. q样再拿Z个已l确定连通的? 扫描它的L? 把被扫过ơ数<3的又标记为已q?.....
l过上面的YY, 法基本上就出来?
把已知肯定与1q通的炚wUCؓS, 剩下不确定的为T. 一开? S中只?q一个点, 其它炚w在T? 每个Ҏ个计数器, 记录自己被扫了多次.
1) 从S中取Z个没处理q的? 把它标记为已处理, q历它的所有邻接点, 被遍历到的点的计数器?1.
2) T中所?计数<S中已处理点个?? 都可以确定是q通的, 把它们从T中删? 加入S?
3) 重复1), 直到S中所有点都处理完.
q时, S中的点就是与1q通的, T中剩下的是不连通的.
复杂度分?
d边和扫描辚w是O(M)?
S集可以用队列l护, dO(N).
T集的l护: 每一轮都要扫一遍当前的T, 把所有计数小的删? 放进S? q样, q一步的d杂度是O(sigma(T)), 会不会到达O(N^2)? 实际上是不会? 因ؓ一条边最多只会一个顶点的计数+1, 因此每一轮还剩在T中的Ҏ不会过q一轮遍历过的边? q样, 所有回合的sigma(T)p定不会超q总边? 因此q里d也是O(M)? 严格来说上W?轮有N-1个点, 也是O(M+N).
q样ȝ复杂度就是O(M)?
不过q个法d用scanf? g++跑了1000+ms, Ҏgetchar才到200+ms? 不知道排前面的神们是不是有更NB的算?
代码:
1 #include
2 #include
3 #include
4 #include
5 #include
6 using namespace std;
7
8 const int MAXN = 10010;
9 const int MAXM = 1000010;
10
11 struct Edge {
12 int v, next;
13 }edg[MAXM*2];
14 int ecnt, gg[MAXN];
15 bool yes[MAXN];
16 int que[MAXN], pq, sq, cnt[MAXN], vt[MAXN], nt;
17 int N, M;
18
19 inline int getnextint()
20 {
21 int r = 0;
22 char c;
23 while(!isdigit(c=getchar()));
24 do{
25 r = r*10 + c-'0';
26 } while(isdigit(c=getchar()));
27 return r;
28 }
29
30 inline void addedge(int u , int v)
31 {
32 edg[ecnt].v = v; edg[ecnt].next = gg[u], gg[u] = ecnt++;
33 edg[ecnt].v = u; edg[ecnt].next = gg[v], gg[v] = ecnt++;
34 }
35
36 int main()
37 {
38 int cas = 0;
39 while(~scanf("%d%d", &N, &M) && !(N==0 && M==0)){
40
41 ecnt = 0;
42 for(int i = 0; i < N; i++){
43 gg[i] = -1;
44 yes[i] = i==0 ? true : false;
45 cnt[i] = 0;
46 vt[i] = i+1;
47 }
48 nt = N-1;
49
50 for(int i = 0; i < M; i++){
51 int u = getnextint();
52 int v = getnextint();
53 addedge(--u, --v);
54 }
55
56 pq = sq = 0;
57 que[sq++] = 0;
58 yes[0] = true;
59
60 while(pq != sq) {
61 int u = que[pq++];
62 for(int e = gg[u]; e >= 0; e = edg[e].next) {
63 if(!yes[edg[e].v])
64 cnt[edg[e].v]++;
65 }
66 for(int i = 0; i < nt; i++) {
67 while(i < nt && cnt[vt[i]] < pq) {
68 yes[vt[i]] = true;
69 que[sq++] = vt[i];
70 if(i < --nt)
71 vt[i] = vt[nt];
72 }
73 }
74 }
75 printf("Case %d: %d\n", ++cas, sq-1);
76
77 }
78 return 0;
79 }

]]>