洛谷 P6938
Posted None
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P6938相关的知识,希望对你有一定的参考价值。
见过的最怪的网络流题,没有之一。
首先新建超级源点,向 \\(1,2\\) 各连 \\(\\infty\\) 的边。设最大流为 \\(A\\),那么显然最优方案中 flutter 和 water 流量之和为 \\(A\\)。
先分析一波答案函数。显然,最终答案关于 flutter 的流量 \\(x\\) 的函数 \\(f(x)=x^a(A-x)^1-a\\)。求导得 \\(f\'(x)=ax^a-1(A-x)^1-a-x^a(1-a)(A-x)^-a\\),化简得 \\(f\'(x)=x^a-1(A-x)^-a(aA-ax-x+ax)\\),解得 \\(f(x)\\) 在 \\((0,aA)\\) 上单调递增,\\((aA,A)\\) 上单调递减。当然,有时候 \\(x\\) 并不能恰好取到 \\(aA\\) —— 显然 flutter 流量上界有最大值 \\(F_max\\),water 流量上界也有最大值 \\(W_max\\),求出 \\(F_max,W_max\\) 之后找 \\([A-W_max,F_max]\\) 中距离 \\(aA\\) 最近的点的位置即可,下文中记这个位置为 \\(F\'\\)。(当然这一步也可以三分,因为代价函数存在严格凸性)
然后很自然的想法是直接以 \\(1\\) 为源点 \\(3\\) 为汇点跑流量限制为 \\(F\'\\) 的最大流,但是这样并不能保证 F 和 W 流量方向相同。考虑调整:先在上面的残量网络中找出每条边的方向,然后只保留这个方向的边,双向改单向,流量为残量网络上这条边已经用掉的流量,然后再跑限流 \\(F\'\\) 的最大流,这样每条边的流量就是 F 的流量,剩余容量就是 W 的流量,感性理解一下保留最大流中的边不影响 \\(1\\) 到 \\(3\\) 的最大流,因此我们的构造是正确的。
const int MAXN=300;
const int MAXM=5e4;
const double INF=1e9;
int n,m;double v,a;
struct edgeint u,v;double w;e[MAXM+5];
int S,T,hd[MAXN+5],to[MAXM*2+5],nxt[MAXM*2+5],ec=1;double cap[MAXM*2+5];
void clear()memset(hd,0,sizeof(hd));ec=1;
void adde(int u,int v,double f)
to[++ec]=v;cap[ec]=f;nxt[ec]=hd[u];hd[u]=ec;
to[++ec]=u;cap[ec]=f;nxt[ec]=hd[v];hd[v]=ec;
int dep[MAXN+5],now[MAXN+5];
bool getdep()
memset(dep,-1,sizeof(dep));queue<int>q;q.push(S);dep[S]=0;
while(!q.empty())
int x=q.front();q.pop();now[x]=hd[x];
for(int e=hd[x];e;e=nxt[e])
int y=to[e];double z=cap[e];
if(!~dep[y]&&z>0)dep[y]=dep[x]+1,q.push(y);
return ~dep[T];
double getflow(int x,double f)
if(x==T)return f;double ret=0;
for(int &e=now[x];e;e=nxt[e])
int y=to[e];double z=cap[e];
if(dep[y]==dep[x]+1&&z>0)
double w=getflow(y,min(f-ret,z));
ret+=w;cap[e]-=w;cap[e^1]+=w;
if(f==ret)return ret;
return ret;
double dinic()
double ret=0;
while(getdep())ret+=getflow(S,INF);
return ret;
void readd_graph()clear();for(int i=1;i<=m;i++)adde(e[i].u,e[i].v,e[i].w);
double Fmax,Wmax,Z,ndF,ndW;int dir[MAXM+5];
int main()
freopen("transport.in","r",stdin);
freopen("transport.out","w",stdout);
scanf("%d%d%lf%lf",&n,&m,&v,&a);
for(int i=1;i<=m;i++)scanf("%d%d%lf",&e[i].u,&e[i].v,&e[i].w);
readd_graph();S=1;T=3;Fmax=dinic();
readd_graph();S=2;T=3;Wmax=dinic();
readd_graph();S=n+1;T=3;adde(S,1,INF);adde(S,2,INF);Z=dinic();
if(Z-Wmax<=a*Z&&a*Z<=Fmax)ndF=a*Z;
else if(Fmax<a*Z)ndF=Fmax;
else ndF=Z-Wmax;
ndW=Z-ndF;
readd_graph();S=n+1;T=3;adde(S,1,ndF);adde(S,2,ndW);dinic();
for(int i=2;i<=ec;i+=2)
double mid=(cap[i]+cap[i^1])/2;
if(cap[i]>mid)dir[i>>1]=-1,cap[i^1]=mid-cap[i^1],cap[i]=0;
else dir[i>>1]=1,cap[i]=mid-cap[i],cap[i^1]=0;
for(int e=hd[n+1];e;e=nxt[e])
int y=to[e];
if(y==1)cap[e]=ndF,cap[e^1]=0;
else cap[e]=cap[e^1]=0;
dinic();
for(int i=2;i<=ec-4;i+=2)
if(dir[i>>1]==1)printf("%.10lf %.10lf\\n",cap[i^1]/v,cap[i]);
else printf("-%.10lf -%.10lf\\n",cap[i]/v,cap[i^1]);
double res=pow(ndF/v,a)*pow(ndW,1-a);
printf("%.10lf\\n",res);
return 0;
洛谷 1485 火枪打怪
【题解】
二分答案+差分
本题要求出尽量小的威力值P,很容易想到二分答案
而本题难点在于二分答案之后的检验。每发子弹对一个区间造成影响,我们可以尝试使用差分。但每发子弹对区间不同位置的影响是不同的。因此我们可以用多阶差分。
a3 | 0 | -p | -p+1 | -p+4 | -p+9 | ... | -p+s2 | 0 | 0 | 0 | 0 |
a2 | 0 | 0 | 1 | 3 | 5 | ... | 2s-1 | p-s2 | 0 | 0 | 0 |
a1 | 0 | 0 | 1 | 2 | 2 | ... | 2 | p-(s+1)2+2 | -p+s2 | 0 | 0 |
a0 | 0 | 0 | 1 | 1 | 0 | ... | 0 | p-(s+1)2 | -2p+2s2+2s-1 |
p-s2 | 0 |
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #define LL long long 5 using namespace std; 6 const int maxn=1000010; 7 LL n,k,l,r,mid,ans,a0[maxn],a1[maxn],a2[maxn],a3[maxn],b[maxn],b2[maxn]; 8 inline LL read(){ 9 LL k=0,f=1; char c=getchar(); 10 while(c<\'0\'||c>\'9\')c==\'-\'&&(f=-1),c=getchar(); 11 while(\'0\'<=c&&c<=\'9\')k=k*10+c-\'0\',c=getchar(); 12 return k*f; 13 } 14 inline LL max(LL x,LL y){return x>y?x:y;} 15 inline LL min(LL x,LL y){return x<y?x:y;} 16 bool check(LL x){ 17 for(int i=0;i<=n+1;i++) a0[i]=a1[i]=a2[i]=a3[i]=0,b[i]=b2[i];//初始化 18 LL num=0,stop=sqrt(x),cnt=0; 19 for (long long i=1;i<=n;i++) { 20 a1[i]+=a1[i-1]+a0[i]; 21 a2[i]+=a2[i-1]+a1[i]; 22 a3[i]+=a3[i-1]+a2[i]; 23 b[i]+=a3[i]; 24 num=(b[i]/x)+ (b[i]%x==0?0:1);//num表示打死i号怪物需要多少枪 25 if (b[i]<=0) continue; 26 if ((cnt+=num)>k) return 0; 27 a3[i]-=num*x; 28 a0[i+1]+=num; a0[i+2]+=num; 29 a0[min(i+stop,n)+1]+=(x-(stop+1)*(stop+1))*num; 30 a0[min(i+stop,n)+2]+=(-2*x+stop*stop+(stop+1)*(stop+1)-2)*num; 31 a0[min(i+stop,n)+3]-=(stop*stop-x)*num; 32 } 33 return cnt<=k; 34 } 35 int main(){ 36 n=read(); k=read(); 37 for(int i=n;i;i--) b[i]=b2[i]=read()+1,r=max(r,b[i]+1LL*(i-1)*(i-1));//先把数组倒过来,方面后面的差分 38 l=0; r<<=1; 39 while(l+1<r) if(check(mid=(l+r)>>1)) r=mid; else l=mid; 40 return printf("%lld\\n",r),0; 41 }
以上是关于洛谷 P6938的主要内容,如果未能解决你的问题,请参考以下文章