1. 程式人生 > >寒武紀camp Day1

寒武紀camp Day1

++i esp long tor closed push hide b- add

補題進度:8/10

A(組合計數)

題意:

  一個人站在數軸原點,每秒有1/4概率向前走一步,1/4概率向後走一步,1/2概率不動,問t秒後在p位置的概率。

  t,p<=100000

分析:

  枚舉不動的個數,於是向前走的個數和向後走的個數都確定了,然後就可組合計數了。

B(平面圖k小割)

題意:

  給出一個n個點的樹,1是根節點,每個點有點權,輸出前k小的包含1節點的連通塊的權值。

  n<=10^5,k<=10^5,點權<=10^9

分析:

  連通塊不好處理,一個連通塊實際上對於一個割集,我們可以這樣轉化:

  把1當作源點,建一個匯點,把每個葉子向匯點連邊,然後樹上每條邊的邊權表示其子樹裏的點權和,那麽這樣一個割就對應了包含S點的一個連通塊的點權和,所以問題就變成了求該圖的k大割

  求k大割是沒法求的,但註意到該問題是平面圖,所以可以建出對偶圖跑k短路。

  對於k短路,可以用可持久化堆來實現。

技術分享圖片
  1 /*
  2 可持久化堆優化k短路
  3 求最短路徑樹:O(nlogn)
  4 求k短路:O(mlogm+klogk)
  5 空間復雜度:O(mlogm),但具體開數組要註意常數,要看執行merge操作的次數
  6 */
  7 #include<bits/stdc++.h>
  8 using namespace std;
  9 const int maxn=100000,maxm=200000,maxsize=3000000;//maxsize是堆的最大個數
10 const long long inf=1000000000000000LL; 11 int a[maxn+5]; 12 long long s[maxn+5]; 13 int n,k,m,len,tot; 14 int S,T; 15 vector<int> g1[maxn+5]; 16 int mi[maxn+5],mx[maxn+5]; 17 int head[maxn+5],nx[maxm+5]; 18 long long dis[maxn+5]; 19 int pos[maxn+5],rt[maxn+5]; 20 struct Edge 21 {
22 int to; 23 long long w; 24 }e[maxm+5]; 25 struct node 26 { 27 /* 28 u是當前堆頂的節點編號 29 key是當前堆頂對應邊的權值,邊的權值定義為:走這條邊要多繞多少路 30 l,r分別是堆左右兒子的地址 31 */ 32 int u; 33 long long key; 34 int l,r; 35 }H[maxsize+5]; 36 struct heapnode 37 { 38 /* 39 求k短路時候用到的數據結構 40 len表示1~倒數第二條邊的邊權和 41 root表示倒數第二條邊的tail的H[tail],其中堆頂就是最後一條邊 42 */ 43 long long len; 44 int root; 45 bool operator < (const heapnode& x) const 46 { 47 return len+H[root].key>x.len+H[x.root].key; 48 } 49 }; 50 priority_queue<heapnode> q; 51 void addedge(int u,int v,long long w) 52 { 53 54 //printf("%d %d %lld\n",u,v,w); 55 e[++len]={v,w}; 56 nx[len]=head[u]; 57 head[u]=len; 58 } 59 void dfs(int k,int fa) 60 { 61 s[k]=a[k]; 62 bool flag=0; 63 mi[k]=n+1; 64 mx[k]=0; 65 for(int i=0;i<g1[k].size();++i) 66 if(g1[k][i]!=fa) 67 { 68 flag=1; 69 dfs(g1[k][i],k); 70 s[k]+=s[g1[k][i]]; 71 mi[k]=min(mi[k],mi[g1[k][i]]); 72 mx[k]=max(mx[k],mx[g1[k][i]]); 73 } 74 if(!flag) mi[k]=mx[k]=++m; 75 } 76 int newnode(int u,long long key) 77 { 78 ++tot; 79 H[tot]={u,key,0,0}; 80 return tot; 81 } 82 int merge(int u,int v) 83 { 84 /* 85 merge兩個堆u和v 86 這裏是采用隨機堆,方便合並,方便持久化 87 */ 88 if(!u) return v; 89 if(!v) return u; 90 if(H[v].key<H[u].key) swap(u,v); 91 int k=++tot; 92 H[k]=H[u]; 93 if(rand()%2) H[k].l=merge(H[k].l,v); 94 else H[k].r=merge(H[k].r,v); 95 return k; 96 } 97 void Kshort() 98 { 99 /* 100 求k短路 101 */ 102 dis[T]=0; 103 for(int i=0;i<T;++i) dis[i]=inf; 104 tot=0; 105 for(int i=m-1;i>=0;--i) 106 { 107 /* 108 DAG圖求最短路徑樹 109 */ 110 int fa=0; 111 for(int j=head[i];j!=-1;j=nx[j]) 112 if(dis[i]>e[j].w+dis[e[j].to]) 113 { 114 dis[i]=e[j].w+dis[e[j].to]; 115 pos[i]=j; 116 fa=e[j].to; 117 } 118 rt[i]=rt[fa]; 119 for(int j=head[i];j!=-1;j=nx[j]) 120 if(j!=pos[i]) 121 { 122 //printf("ce : %d %d\n",i,e[j].to); 123 rt[i]=merge(rt[i],newnode(e[j].to,e[j].w+dis[e[j].to]-dis[i])); 124 } 125 } 126 //printf("tot : %d\n",tot); 127 //printf("len : %d\n",len); 128 //printf("m : %d\n",m); 129 //for(int i=0;i<=T;++i) printf("%d : %lld %d\n",i,dis[i],pos[i]); 130 printf("%lld\n",dis[S]+s[1]); 131 heapnode now={0LL,rt[S]}; 132 if(now.root) q.push(now); 133 while(--k&&!q.empty()) 134 { 135 /* 136 每次從優先隊列隊首取出最小的邊集 137 每個邊集對對應一種合法的k短路走法 138 有兩種擴展方法 139 第一種:將最後一條邊換成所在堆的次小元素(相當於從堆裏把堆頂刪除) 140 第二種:新加一條邊,即從最後一條邊繼續往後走 141 */ 142 now=q.top(); 143 q.pop(); 144 printf("%lld\n",now.len+H[now.root].key+dis[S]+s[1]); 145 int id=merge(H[now.root].l,H[now.root].r); 146 //printf("%d : %d %lld\n",id,H[id].u,H[id].key); 147 if(id) 148 q.push({now.len,id}); 149 now.len+=H[now.root].key; 150 if(rt[H[now.root].u]) 151 q.push({now.len,rt[H[now.root].u]}); 152 } 153 } 154 int main() 155 { 156 srand(time(0)); 157 scanf("%d%d",&n,&k); 158 for(int i=1;i<=n;++i) scanf("%d",&a[i]); 159 for(int i=1;i<n;++i) 160 { 161 int u,v; 162 scanf("%d%d",&u,&v); 163 g1[u].push_back(v); 164 g1[v].push_back(u); 165 } 166 dfs(1,0); 167 for(int i=0;i<=n+1;++i) head[i]=-1; 168 for(int i=2;i<=n;++i) addedge(mi[i]-1,mx[i],-s[i]); 169 for(int i=0;i<m;++i) addedge(i,i+1,0); 170 S=0,T=m; 171 Kshort(); 172 return 0; 173 }
View Code

C(隨機算法)

題意:

  給出一個n個點的樹,每個點有自己的顏色,選出一個包含點數最少的連通塊,使得其中有k種不同的顏色。

  n<=10000,k<=5,每個點顏色<=n

分析:

  如果每個點的顏色是1~5,那就很好辦了,我們只需要做樹形dp就可以了,dp[i][j]表示以i為根的子樹,顏色包含情況為j的最少點數

  那麽這個dp就是n*4^5的

  但現在顏色數量有很多,無法表示狀態

  考慮隨機算法,我們將1~n顏色隨機映射到1~k,我們來分析下正確的概率:

  分母很明顯是$k^n$

  成功當且僅當作為答案的那一組顏色被染成了k種不同的顏色,所以分子就是$k!*k^{n-k}$

  所以成功的概率是$\frac{k^n}{k!*k^{n-k}} = 0.2$

  於是隨個30次就行了

寒武紀camp Day1