1007 tree (hdu 6820)

题意:

输入 n,kn,kn,k ,给定一棵带权树,包含 nnn 个节点,要求找到一个权重和最大的子树,使得这个子树度数大于 kkk 的节点不超过 111 个。

(n<=2e5)(n<=2e5)(n<=2e5)

思路:

思考枚举度数大于k的点,进行搜索?显然复杂度太高。贪心求解也不能枚举到每一颗子树。

参考题解的方法,使用 dp[i][0]dp[i][0]dp[i][0] 表示包含 iii 的子树不包含度数大于 kkk 的节点的最大权和,使用 dp[i][1]dp[i][1]dp[i][1] 表示包含 iii 的子树只有 111 个度数大于 kkk 的节点的最大权和,并且方程默认 iii 不为答案子树的根,即 iii 还会向上连边。

考虑转移方程,假设目前节点为 uuu ,且其子节点为 v1,v2,v3,...,vmv_1,v_2,v_3,...,v_mv1​,v2​,v3​,...,vm​ ,分别的边权值为 w1,w2,w3,...,wmw_1,w_2,w_3,...,w_mw1​,w2​,w3​,...,wm​

那么考虑 dp[u][0]dp[u][0]dp[u][0] ,那么由于其要向上连边,所以 dp[u][0]=dp[u][0]=dp[u][0]= 最大的 k−1k-1k−1 个 (dp[vi][0]+wi)(dp[v_i][0]+w_i)(dp[vi​][0]+wi​) 之和

对于 dp[u][1]dp[u][1]dp[u][1] ,分别考虑两种情形,若大于 kkk 的节点为 uuu ,那么 dp[u][1]=∑im(dp[vi][0]+wi)dp[u][1]=\sum_{i}^m(dp[v_i][0]+w_i)dp[u][1]=∑im​(dp[vi​][0]+wi​) ,若大于 kkk 的节点不为 uuu ,那么就需要选择 k−2k-2k−2 个 dp[vi][0]dp[v_i][0]dp[vi​][0] 和 111 个 dp[vi][1]dp[v_i][1]dp[vi​][1] 并且使得它们的和最大,可以贪心的枚举 dp[vi][1]dp[v_i][1]dp[vi​][1] 求解。

现在需要考虑更新答案,那么就需要在假设 uuu 为子树的根来计算答案

如果m>=k 那么 ans1=dp[u][0]+ans1 = dp[u][0] +ans1=dp[u][0]+ 第 kkk 大的 dp[vi][0]+widp[v_i][0]+w_idp[vi​][0]+wi​ ,否则 ans1=dp[u][0]ans1=dp[u][0]ans1=dp[u][0] ,因为不用上连,可以多一个子树

那么 ans2ans2ans2 就需要选择 k−1k-1k−1 个 dp[vi][0]dp[v_i][0]dp[vi​][0] 和 111 个 dp[vi][1]dp[v_i][1]dp[vi​][1] 并且使得它们的和最大,和上面类似,还要和 ∑im(dp[vi][0]+wi)\sum_{i}^m(dp[v_i][0]+w_i)∑im​(dp[vi​][0]+wi​) 取一个最大值。

那么 ans=max(ans1,ans2)ans=max(ans1,ans2)ans=max(ans1,ans2)

代码:

#include <bits/stdc++.h>
#define P pair<int,int>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
using namespace std;
const int N=2e5+10;
int T;
int n,k;
ll ans,dp[N][2];
vector<P>e[N];
bool cmp(pair<ll,ll> a,pair<ll,ll> b) {return a.fi>b.fi;
}
void dfs(int u,int f) {dp[u][0]=dp[u][1]=0;vector<pair<ll,ll>>s;for(auto v:e[u]) {if(v.fi==f) continue;dfs(v.fi,u);s.pb(mp(dp[v.fi][0]+v.se,dp[v.fi][1]+v.se));}int g=s.size();sort(s.begin(),s.end(),cmp);ll sum=0;for(auto v:s) sum+=v.fi;dp[u][1]=sum;for(int i=0;i<min(k-1,g);i++) dp[u][0]+=s[i].fi;ll ans1=0,ans2=dp[u][1];if(g>=k) ans1=dp[u][0]+s[k-1].fi;else ans1=dp[u][0];ll temp=0;for(int i=0;i<min(k,g);i++) temp+=s[i].fi;for(int i=0;i<g;i++) {if(i<k) ans2=max(ans2,temp-s[i].fi+s[i].se);else if(k>0) ans2=max(ans2,temp-s[k-1].fi+s[i].se);if(i<k-1) dp[u][1]=max(dp[u][1],dp[u][0]-s[i].fi+s[i].se);else if(k>1)dp[u][1]=max(dp[u][1],dp[u][0]-s[k-2].fi+s[i].se);}ans=max(ans,max(ans1,ans2));
}
int main()
{scanf("%d",&T);while(T--) {scanf("%d%d",&n,&k);ans=0;for(int i=1;i<=n;i++) e[i].clear();for(int u,v,w,i=1;i<n;i++) {scanf("%d%d%d",&u,&v,&w);e[u].pb(mp(v,w));e[v].pb(mp(u,w));}if(k==0) {puts("0");continue;}dfs(1,0);//for(int i=1;i<=n;i++) cout<<i<<" "<<dp[i][0]<<" "<<dp[i][1]<<endl;printf("%lld\n",ans);}}
/*
2
9 1
1 2 5
1 9 3
2 5 4
2 3 2
2 4 3
4 8 7
3 7 6
3 6 4
5 2
1 2 5
2 3 2
2 4 3
2 5 4
*/

1008 Set2 (hdu 6821)

题意:

输入 n,kn,kn,k,给一个 1−n1-n1−n 的序列,然后每次删掉数组中最小的数字,然后再随机删除 kkk 个数字,直到序列中的数字小于等于 kkk 个,问每个数字留下的概率是多少。

(n<=5000)(n<=5000)(n<=5000)

思路:

考虑使用 n2n^2n2 的算法来解决,设 dp[i]dp[i]dp[i] 表示倒数第 iii 个数字留下的概率,那么我们可以从后往前添加数字,并且更新答案。

首先是计算出最后剩下的数字个数为 r=n%(k+1)r=n\%(k+1)r=n%(k+1) ,就将倒数前 rrr 个的概率初始化为 111 。

接下来往前考虑每一个数字加入, 若 iii 必然被删除,则不更新其他数的概率,否则假设此时有 iii 个数字在序列之中,对于 jjj 的概率该如何计算。

dp[j]=dp[j]∗i−ji+dp[j−1]∗j−1idp[j]=dp[j]*\frac{i-j}{i}+dp[j-1]*\frac{j-1}{i}dp[j]=dp[j]∗ii−j​+dp[j−1]∗ij−1​

因为首先保留了最后剩下的数字个数,也就是说在每次加入数字时,都会有一个数字被删除,因为 jjj 要留下,此时删除数字可以 [i,j)[i,j)[i,j) ,或者是 [j+1,1][j+1,1][j+1,1] 中的一个数字。

要注意的是,在删除后面的数字时,那么倒数第 jjj 个数字就变成了倒数第 j−1j-1j−1 个数字,所以要用 dp[j−1]dp[j-1]dp[j−1] 的值进行更新

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10;
const int mod=998244353;
int T;
int n,k;
int ksm(int a,int b) {int res=1,t=a;while(b) {if(b&1) res=1ll*t*res%mod;t=1ll*t*t%mod;b>>=1;}return res;
}
int inv[N],dp[N];
int main()
{scanf("%d",&T);inv[0]=1;for(int i=1;i<N;i++) inv[i]=ksm(i,mod-2);while(T--) {scanf("%d%d",&n,&k);int r=n%(k+1);for(int i=1;i<=r;i++) dp[i]=1;for(int i=r+1;i<=n;i++) {dp[i]=0;if((n-i)%(k+1)==0) continue;for(int j=i;j>=1;j--)dp[j]=(1ll*dp[j]*(i-j)%mod*inv[i]+1ll*dp[j-1]*(j-1)%mod*inv[i])%mod;}for(int i=n;i>=1;i--) {printf("%d%c",dp[i],i==1?'\n':' ');}}}

1011 Eaxm (hdu 6824)

题意:

输入 nnn ,给 nnn 场考试,每场考试有两个时间段可以通过,这两个时间段必须选一个,不能同时选,并且要使得参加所有考试的时间段不重合,问最短什么时候结束 nnn 场考试。

思路:

由于是两个时间段,必须选一个,不是选 iii 就是选 i‘i‘i‘ ,考虑使用 2−sat2-sat2−sat ,但是只能判定存在性,不能求出最小值。

所以可以采用二分时间的方法来求出最短的时间。

首先将 2∗n2*n2∗n 个时间段按照 rrr 从小到大排序,然后对于每一个 iii ,考虑所有的 j<ij<ij<i ,使得 T[j].r>=T[i].lT[j].r>=T[i].lT[j].r>=T[i].l ,那么就连边 i−>j′,j−>i′i->j',j->i'i−>j′,j−>i′ 。这样就可以建出所有的冲突情况。但是这样建边是 O(n2)O(n^2)O(n2) 的会超时,发现 jjj 的范围始终是一个连续的区间,即可以使用线段树优化建边的方式,使用 O(nlogn)O(nlogn)O(nlogn) 的时间建出图。

假设当前枚举的时间为x,那么对于所有的时间 i,T[i].r>xi,T[i].r>xi,T[i].r>x ,那么必须选择 i′i'i′ ,那么连边 i−>i′i->i'i−>i′

那么每一次判断可行,就跑一次 tarjantarjantarjan ,判断 iii 和 i′i'i′ 是否在一个强连通之中就行了。

细节较多,需要处理好图节点和线段树节点的关系,并且在每次判断是需要初始化。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
using namespace std;
const int N=1e6+10;
int rc[N*4],lc[N*4];
int n,T,cnt,rt;
struct node{int fi,se,id;friend bool operator <(node a,node b) {return a.se<b.se;}
}L[N];vector<int>e[N];
inline void add(int u,int v) {e[u].pb(v);}
inline void del(int u) {e[u].pop_back();   }
inline int id(int u,int op) {return op==1?n+u:n+cnt+u;}
inline int inv(int u) {  return ((u-1)^1)+1;}
void built(int &o,int l,int r) {if(o==0) {o=++cnt,lc[o]=0,rc[o]=0;}if(l==r) {return ;}int mid=(l+r)/2;built(lc[o],l,mid);built(rc[o],mid+1,r);
}
void up(int o,int l,int r) {if(l==r) {add(id(o,1),inv(L[l].id));add(L[l].id,id(o,2));return ;}add(id(o,1),id(lc[o],1));add(id(o,1),id(rc[o],1));add(id(lc[o],2),id(o,2));add(id(rc[o],2),id(o,2));int mid=(l+r)/2;up(lc[o],l,mid);up(rc[o],mid+1,r);
}
void addedge(int o,int l,int r,int LL,int RR,int pos){if(LL<=l&&RR>=r){add(pos,id(o,1));add(id(o,2),inv(pos));return;}int mid=(l+r)>>1;if(LL<=mid)addedge(lc[o],l,mid,LL,RR,pos);if(RR>mid)addedge(rc[o],mid+1,r,LL,RR,pos);
}
struct TARJAN{int sz,dfn[N],low[N],vis[N],co[N],nscc;stack<int> s;void init(int mx){for(int i=0;i<=mx;i++) vis[i]=0,dfn[i]=0;while(!s.empty()) s.pop();nscc=sz=0;}void tarjan(int u){dfn[u]=low[u]=++sz; vis[u]=1; s.push(u);for(auto v:e[u]){if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(vis[v]&&low[u]>dfn[v]) low[u]=dfn[v];}if(low[u]==dfn[u]){nscc++;while(1){int id=s.top(); s.pop();vis[id]=0;co[id]=nscc;if(id==u) break;}}}
}ta;
int find(int x) {int l=1,r=n,ans=n;while(l<=r) {int mid=(l+r)/2;if(L[mid].se>=x) ans=mid,r=mid-1;else l=mid+1;}return ans;
}
bool check(int x) {ta.init(cnt*2+n);for(int i=1;i<=n;i++) if(L[i].se>L[x].se) add(L[i].id,inv(L[i].id));for(int i=1;i<=n+cnt*2;i++) if(!ta.dfn[i]) ta.tarjan(i);for(int i=1;i<=n;i++) if(L[i].se>L[x].se) del(L[i].id);for(int i=1;i<=n;i+=2) if(ta.co[i]==ta.co[i+1]) return 0;return 1;
}
int main()
{scanf("%d",&T);while(T--) {scanf("%d",&n);n*=2;rt=0;cnt=0;for(int i=1;i<=n;i++) {int l,r;scanf("%d%d",&l,&r);L[i].fi=l,L[i].se=l+r;L[i].id=i;}sort(L+1,L+n+1);built(rt,1,n);up(rt,1,n);for(int i=1;i<=n;i++) {int pos=find(L[i].fi);//cout<<pos<<endl;if(pos<i) {addedge(rt,1,n,pos,i-1,L[i].id);}}int ans=n+1,l=1,r=n;while(l<=r) {int mid=(l+r)/2;if(check(mid)) ans=mid,r=mid-1;else l=mid+1;}printf("%d\n",ans==n+1?-1:L[ans].se);for(int i=0;i<=cnt*2+n;i++) e[i].clear();}
}

2020 杭电多校5 1007、1008、1011相关推荐

  1. 杭电多校第七场 1011 Kejin Player HDU(6656)

    杭电多校第七场 1011 Kejin Player 题意:给你N行,代表从i级有花费a[i]元的r[i]/s[i]的概率达到i+1级,剩下的概率中可能会到达x[i]级.然后询问从L级到R级的花费会是多 ...

  2. 2020杭电多校训练(第五、六场)

    目录 第五场 1001.Tetrahedron 1009.Paperfolding 1003.Boring-Game 1012.Set1 1007.Tree 第六场 1006.A-Very-Easy- ...

  3. 点分治问题 ----------- HDU6881 Tree Cutting or 2020杭电多校第10场 [点分治+思维]

    题目链接 题目大意: 给定nnn个节点的树,问删除尽可能小的点使得树的直径不超过KKK,输出最小删除的点数,(1<=k<=n<=3e5)(1<=k<=n<=3e5) ...

  4. 欧拉降幂 ---- 2020 杭电多校[E - Fibonacci Sum]+欧拉降幂+和式的调整+二次剩余+毒瘤卡常

    解题思路: 首先你得知道斐波那契的通项式子:首先你得知道斐波那契的通项式子:首先你得知道斐波那契的通项式子: F(n)=15[(1+52)n−(1−52)n]F(n) = {1\over\sqrt5} ...

  5. 2020杭电多校(二) New Equipments(最小费用最大流)

    New Equipments 思路 数据已经有提示了b∗b<=4∗a∗cb * b <= 4 * a * cb∗b<=4∗a∗c,这意味着,每一个a,b,ca, b, ca,b,c构 ...

  6. 2020杭电多校第六场 A Very Easy Math Problem 莫比乌斯反演 (HDU 6833)

    A Very Easy Math Problem 题解 ∑ a 1 = 1 n ∑ a 2 = 1 n ⋅ ⋅ ⋅ ∑ a x = 1 n ( ∏ j = 1 x a j k ) f ( g c d ...

  7. 2020杭电多校训练(第一、二场)

    目录 第一场 1005.Fibonacci-Sum 1009.Leading-Robots 1006.Finding-a-MEX 第二场 1012.String-Distance 1005.New-E ...

  8. 【2020杭电多校】 Lead of Wisdom 【暴搜】

    这道题,我还以为是什么状压dp(看到dp头就疼)? 我是不是脑瘫了,一开始想过暴搜,就这?最坏时间复杂度跑一遍早就超过1e8了,一秒肯定跑不完,能过?弃了弃了,谁知道题目给了8000ms!!! 什么是 ...

  9. 2020杭电多校第二场 Lead of Wisdom(爆搜)

    Problem Description In an online game, "Lead of Wisdom" is a place where the lucky player ...

最新文章

  1. TP 框架实现支付宝接口功能
  2. JAVA网络编程之Socket
  3. php写文件 效率,php中读写文件与读写数据库的效率_PHP教程
  4. 看了数百个PPT封面,我只想告诉你这两个套路!
  5. 吴恩达:机器学习应以数据为中心
  6. python网页调用摄像头_Python调用摄像头
  7. 计算机一级windows2000,一级BWindows2000操作系统[2]
  8. 鸿合科技成为AMX品牌全系列产品中国(大陆)总代理
  9. fmt—fmt:formatDate的输出格式
  10. Boost-IO学习 异步数据处理Simple(转)
  11. matlab数据类型的转换方法
  12. 播布客全部视频教程汇总
  13. SpringSocial整合QQ授权登录
  14. 解决微信小程序内下载视频失败wx.saveVideoToPhotosAlbum :fail invalid video
  15. python 爬取携程旅游景点评论
  16. 解决vscode电脑卡顿问题
  17. win10无法访问xp计算机,XP系统访问Win10打印机被拒绝的解决方法
  18. VS 和VC 的区别
  19. 程序员的十层楼,比尔盖茨仅第四层,你能到第几层?
  20. fedora nginx php,在Fedora 24服务器和工作站上使用MariaDB和PHP / PHP-FPM设置Nginx

热门文章

  1. unity3d shader之God Ray上帝之光
  2. 工作中遇到的印象深刻的Bug(APP端)
  3. matlab独立t样本检验,matlab与单样本t检验
  4. 海义QQ群共享下载者的一些图例使用步骤教程
  5. 钱理群: 真正的鲁迅是沉默的
  6. 数学建模 之 ARCH模型和GARCH模型
  7. 学习MySQL之数据库简介
  8. 卷三十一 汉纪二十三
  9. 关于USB CCID 协议的一些英文缩写
  10. hazelcast java_JVM内存级分布式缓存Hazelcast