寒武纪camp Day1

Posted 人活着就是为了Chelly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了寒武纪camp Day1相关的知识,希望对你有一定的参考价值。

补题进度: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的主要内容,如果未能解决你的问题,请参考以下文章

寒武纪camp Day3

寒武纪camp Day6

寒武纪camp Day4

寒武纪camp Day5

寒武纪camp Day2

Aiiage Camp Day1 E Littrain wanna be small