[APIO2016]
2016的题貌似是韩国棒子出的,好丧啊.... 看了题解还想了好久......
-------------------------------------------------
A.Boat
有n个数,每个数字可取[li,ri]内的任意整数si,但是要求对于任意i<j,都有si<sj,求方案数 n<=500,l,r<=10^9
题解:首先离散,然后不同区间的方案数很好转移,我们考虑相同区间的方案数,发现是一个差分了多次的数列,如果区间长度是l,选m个这样的区间,那么方案数是11111...差分m次后的第l项。然后我们可以发现这个其实是一个组合数,等于C(m,m+l)。我们用f[i][j]表示第i个选第j个区间的方案数,然后我们把f数组前缀和之后推出公式,
f[i][j]=∑C(i-i'-1,i-i'+l-1) * f[i'-1][j-1]
复杂度n^3
#include<iostream> #include<cstdio> #include<algorithm> #define mod 1000000007 #define MAXN 1000 #define int long long using namespace std; inline int read() {int x = 0 , f = 1; char ch = getchar();while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f; }int tot=-1,n,L[2*MAXN+5]; int l2[MAXN*2+5],l[MAXN+5],r[MAXN+5]; int f[MAXN+5][MAXN*2+5]; int inv[MAXN+5],p[MAXN+5];main() {p[0]=inv[0]=p[1]=inv[1]=1;for(int i=2;i<=MAXN;i++){p[i]=1LL*p[i-1]*i%mod;inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;}//for(int i=2;i<=MAXN;i++) inv[i]=1LL*inv[i]*inv[i-1]%mod;n=read();for(int i=1;i<=n;i++){l[i]=l2[i*2-1]=read();r[i]=l2[i<<1]=read();l2[i<<1]+=1;}sort(l2+1,l2+n*2+1);for(int j=1;j<=n*2;j++)if(l2[j]!=l2[j-1])l2[++tot]=l2[j]; tot++;for(int i=1;i<tot;i++) L[i]=l2[i]-l2[i-1]; for(int i=1;i<=n;i++){ l[i]=upper_bound(l2,l2+tot,l[i])-l2;r[i]=upper_bound(l2,l2+tot,r[i])-l2;// cout<<l[i]<<" "<<r[i]<<endl; }for(int i=0;i<tot;i++) f[0][i]=1;for(int i=1;i<=n;i++){f[i][0]=1;for(int j=l[i];j<=r[i];j++){f[i][j]=(long long)L[j]*f[i-1][j-1]%mod;//cout<<f[i][j]<<endl;int now=1;long long c=L[j]-1;for(int k=i-1;k;--k)if(l[k]<=j&&j<=r[k]){now++;c=c*(long long)(L[j]+now-2)%mod*inv[now]%mod;if(!c)break;f[i][j]=(f[i][j]+(long long)f[k-1][j-1]*c)%mod;}}for(int j=1;j<tot;j++)f[i][j]=((long long)f[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1]+mod)%mod;// for(int j=1;j<tot;j++)// cout<<i<<" "<<j<<" "<<f[i][j]<<endl; }cout<<(long long)(f[n][tot-1]-1+mod)%mod;return 0; }
B.给定一棵n个非叶节点,m个叶节点的树,有边权,定义修改边权的费用为前后边权的差的绝对值,你要让所有叶节点到根节点的距离相同,但又不能把边权改成负数,求最小费用。
n,m<=300000
题解:我们用f[i][j]表示第i个点,子树中的叶节点到它的距离都是j的最小费用,那么f[i][0]=∑Wjk (jk都在子树i中)。
很显然,f函数是一个下凸的函数,并且存在一些拐点,拐点前后斜率变化是1,但是拐点可以重在某一个点上。只有一个叶节点时,拐点有两个,且都为于0,凸壳形状像一个绝对值函数。
所以我们只要知道所有拐点,就可以知道这个函数啦。
我们考虑向一个子树添加边时候的影响,由于w可以无限增大,在一定大小后只修改这一条边一定最优,斜率肯定都是1,所以对于斜率大于1的部分我们都可以舍去,即弹掉所有原来斜率大等0的拐点。
这样一次合并我们实际上只把它向右平移了一下。需要删除和合并操作,所以写一个可并堆就好啦。
复杂度(n+m)log(n+m)
#include<iostream> #include<cstdio> #define MN 600000 #define ll long long using namespace std; inline int read() {int x = 0 , f = 1; char ch = getchar();while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f; }int n,m,fa[MN+5],in[MN+5]; ll w[MN+5],ans=0;struct Heap{Heap *l,*r;ll p;int d;Heap(ll _p):p(_p),l(0),r(0),d(1){};inline friend int dis(Heap*x){return x?x->d:0;}friend Heap* Merge(Heap*x,Heap*y){if(!x) return y;if(!y) return x;if(x->p<y->p) swap(x,y);x->r=Merge(x->r,y);if(dis(x->r)>dis(x->l)) swap(x->l,x->r);x->d=dis(x->r)+1;return x;} }*s[MN+5],*a,*b;int main() {n=read();m=read();n+=m;for(int i=2;i<=n;i++){++in[fa[i]=read()];ans+=(w[i]=read());}for(int i=n;i>1;i--){if(!s[i]) s[i]=Merge(new Heap(0),new Heap(0));for(int j=1;j<in[i];j++)s[i]=Merge(s[i]->l,s[i]->r);a=new Heap(s[i]->p+w[i]);s[i]=Merge(s[i]->l,s[i]->r);b=new Heap(s[i]->p+w[i]);s[i]=Merge(s[i]->l,s[i]->r);s[fa[i]]=Merge(s[fa[i]],Merge(s[i],Merge(a,b)));}int top=0;while(s[1])w[++top]=s[1]->p,s[1]=Merge(s[1]->l,s[1]->r);w[top+1]=0;for(int i=top;m;m--,i--) ans-=(w[i]-w[i+1])*m;printf("%lld\n",ans);return 0; }
C.Gap
给定一个长度为n的严格递增数列,你每次可以询问一个数字区间的最大值,最小值,求最大差分。n<=100000
题解:对于subtask1,询问次数不超过(n+1)/2,我们枚举左右节点查,然后缩短这个区间就好啦。
对于subtask2,询问的区间含有k个数时费用是k+1,要让费用不超过3n。我们先求最大值x和最小值y,显然答案不会低于(y-x)/(n-1),所以我们把数字分块,每块内不存在答案,都询问一次就行了。
#include "gap.h" #include<algorithm> #include<iostream> using namespace std; #define ll long long #define INF 1000000000000000000LL ll s[200005]; ll ans=0; int cnt=0;ll solve(int x) {ll l,r;MinMax(0,INF,s+1,s+x);cnt=2;int i=2,j=x-1;for(l=s[1]+1,r=s[x]-1;i<=j;l=s[i++]+1,r=s[j--]-1){MinMax(l,r,s+i,s+j);}for(int i=2;i<=x;i++)ans=max(ans,s[i]-s[i-1]);return ans; }ll findGap(int T, int N) {if(T==1) return solve(N);if(N==1) return 0;MinMax(0,INF,s+1,s+2);cnt=2;ll p=(s[2]-s[1]-1)/(N-1)+1;for(ll i=s[1]+1;i<=s[2]-1;i+=p){MinMax(i,min(i+p-1,s[2]-1),s+cnt+1,s+cnt+2);if(s[cnt+1]>0)cnt+=2;}sort(s+1,s+cnt+1);for(int i=2;i<=cnt;i++)ans=max(ans,s[i]-s[i-1]);return ans; }
转载于:https://www.cnblogs.com/FallDream/p/apio2016.html
[APIO2016]相关推荐
- Loj #2568. 「APIO2016」烟花表演
Loj #2568. 「APIO2016」烟花表演 题目描述 烟花表演是最引人注目的节日活动之一.在表演中,所有的烟花必须同时爆炸.为了确保安全,烟花被安置在远离开关的位置上,通过一些导火索与开关相连 ...
- 「APIO2016」烟花表演
「APIO2016」烟花表演 解题思路 又是一道 solpe trick 题,观察出图像变化后不找一些性质还是挺难做的. 首先令 \(dp[u][i]\) 为节点 \(u\) 极其子树所有叶子到 \( ...
- APIO2016 Fireworks
APIO2016 Fireworks 题意: 题目传送门 题解: 第一眼想到的应该是一个\(Dp\),我们记\(f_{u, i}\)表示\(u\)这个子树中,所有叶子节点到\(u\)的距离都为\(i\ ...
- [APIO2016]划艇
[APIO2016]划艇 总共只有2*n段.分段进行DP 简单的方法是: 外层枚举段数j,f[i]表示,当前枚举到j的时候,以(i,j)结尾(必须选择(i,j))的方案数,枚举一个f(p,1~j-1) ...
- BZOJ 4584 [Apio2016]赛艇
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=4584 题解 首先将BiB_iBi加111,把派出的数量变成左闭右开的区间,将AiA_i ...
- 【APIO2016】Fireworks【闵可夫斯基和】【凸包向量和】【可并堆】
题意:给一棵带边权的树,可以花费 111 的代价把一条边的边权修改 111,一条边可以修改多次,求使得根到叶子距离相等的最小代价. n≤3×105n\leq 3\times 10^5n≤3×105 先 ...
- [APIO2016] 划艇(dp + 组合数 + 前缀和优化)
problem luogu-P3643 solution 有个显然的暴力 dpdpdp.设 dp(i,j):dp(i,j):dp(i,j): 到了第 iii 个学校,其参加且派出 jjj 个划艇的方案 ...
- P3642 [APIO2016]烟火表演(左偏树、函数)
解析 感觉是左偏树的神题了. 首先有一个比较显然的结论,一个合法的方案中,两个叶子到它们 lca\text{lca}lca 的距离必须相等. 考虑设计 dp\text{dp}dp : fi,xf_{i ...
- [APIO2016]烟火表演
链接:https://www.luogu.org/problemnew/show/P3642 跟上一道题类似但更难,首先也是观察出在某个节点代价是下凸的函数,并且得到转移方程: 1.x<=L f ...
最新文章
- c java utf 8_如何对javac设置encoding utf 8
- 1、SpringBoot------表单校验
- 201521123054《Java程序设计》第1周学习总结
- 通用计划明年推出自动驾驶出租车共享服务,可定制化设计车辆
- 心情舒畅,升级到u10.04了
- Android TelephonyManager类
- i.MX6 设备树 GPIO 默认值
- Opencv中convertTo函数
- HDOJ1008 Elevator
- 实验01 使用网络协议分析仪Wireshark
- kux文件怎么打开 苹果手机如何观看kux视频
- 腾讯日常实习面试经历
- nova Evacuate
- 6.S081参考书笔记 —— 第7章调度、进程交互
- java基于ssm的自助旅游管理系统
- MySQL中查看数据库
- CC2640R2F之配对绑定与解除绑定篇
- MySQL中Invalid default value错误解决方法
- matlab 双y轴对数坐标 误差线,matlab双y轴添加误差棒(转载)
- varchar 和 char 的区别
热门文章
- multi task训练torch_手把手教你使用PyTorch(2)-requires_gradamp;computation graph
- python中_python中的一些用法总结
- 基于matlab的车流量检测,求解释代码,一段交通车流量检测的代码
- imagex使用方法_Microsoft OneDrive 的使用心得,真香
- Java基础学习-IO流
- poj 1083 Moving Tables
- 十五. Python基础(15)--内置函数-1
- [Unity3D]unity3d5.0简单的调用摄像头
- mysql outfile csv_sql-MySQL导出到outfile:CSV转义字符
- 9、Flutter 实现 生成二维码