「NOIP模擬」【2018.11.6晚間訓練賽】ping【樹狀數組】【樹上點差分】【dfs序】

問題描述

TgopknightTgopknight所連接的網絡共有nn個站點,由於經費問題,每兩個站點之間有且僅有一條線路,這些站點中有一些損壞了,TgopknightTgopknight進行了kk次測試,每次測試兩個站點之間是否連通,由於TgopknightTgopknight手氣太好,他每次測試的兩個站點之間都不連通。TgopknightTgopknight現在想知道最少有多少個站點損壞了,並想知道一種可能的損壞數最小的損壞情況。


考慮一條路徑目前沒有選擇任何點,我們選擇哪一個點是最優的。

求出這棵樹的dfsdfsdfn[]dfn[]

對於每個詢問,即路徑,我們可以看做是在dfsdfs序上的區間。

區間記錄33個量,x,y,lcax,y,lca,容易理解變量名。

我們發現,選擇lcalca是最優秀的策略。易於理解,對於每個區間,lcalca所在的位置更容易影響到其他區間。

現在的問題在於如何進行操作。

既然已經求出了dfsdfs序,所以我們就在這上面進行修改。

由於我們是記錄了某個lcalca的使用情況,而直接影響到的就是當前區間,所以我們就考慮用樹上點差分進行操作。

關於普通點差分的執行:sum[x]++,sum[y]++,sum[lca],sum[fa[lca]]sum[x]++,sum[y]++,sum[lca]--,sum[fa[lca]]--

由於是區間上進行修改與查詢,所以我們再添加一個樹狀數組。

另外貪心一下,查詢前將lcalca按照dfsdfs序先排個序。

#include <stdio.h>
#include <algorithm>
using namespace std;

const int N=3e5+5;

int dep[N],fa[N][21];
int inc,to[N],nxt[N],head[N];
int n,m,k,ans,tim,l[N],r[N],vis[N],dfn[N],bit[N];

struct node {
	int x,y,lca;
}ask[N];

#define lowbit(x) (x&(-x))

void swap(int &x,int &y) {
	int ret=y;y=x;x=ret;
}

void ins(int x,int y) {
	to[++inc]=y;nxt[inc]=head[x];head[x]=inc;
}

void add(int x,int y) {
	while(x<=n) bit[x]+=y,x+=lowbit(x);
}

int getsum(int x) {
	int ret=0;while(x) ret+=bit[x],x-=lowbit(x);return ret;
}

void dfs(int x) {
	dfn[x]=++tim;l[x]=tim;dep[x]=dep[fa[x][0]]+1;
	for(int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=head[x];i;i=nxt[i]) if(to[i]!=fa[x][0]) fa[to[i]][0]=x,dfs(to[i]);r[x]=tim;
}

int lca(int x,int y) {
	if(dep[x]<dep[y]) swap(x,y);int dlt=dep[x]-dep[y];
	for(int i=20;i>=0;i--) if(dlt&(1<<i)) x=fa[x][i];
	if(x==y) return x;
	for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}

bool cmp(node x,node y) {
    return dfn[x.lca]>dfn[y.lca];
}

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) {
    	int x,y;
    	scanf("%d%d",&x,&y);ins(x,y);ins(y,x);
	}
    dfs(1);
    scanf("%d",&k);
    for(int i=1;i<=k;i++) scanf("%d%d",&ask[i].x,&ask[i].y),ask[i].lca=lca(ask[i].x,ask[i].y);
    std::sort(ask+1,ask+1+k,cmp);
    for(int i=1;i<=k;i++) {
    	int x=ask[i].x,y=ask[i].y,lc=ask[i].lca;
    	int ret1=getsum(dfn[x]),ret2=getsum(dfn[y]);
    	int ret3=getsum(dfn[lc]),ret4=getsum(dfn[fa[lc][0]]);
    	if(ret1+ret2-ret3-ret4<=0) {
    		ans++;vis[lc]=1;add(dfn[lc],1);add(r[lc]+1,-1);
		}
	}
	printf("%d\n",ans);
	for(int i=1;i<=n;i++) if(vis[i]) printf("%d ",i);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章