补题进度: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 }
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次就行了