洛谷 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 }
View Code

 

以上是关于洛谷 P6938的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P1055 ISBN号码

洛谷题目怎么看答案

洛谷的u id是啥

洛谷 P1193 洛谷团队训练VS传统团队训练

洛谷可以用手机刷题吗

洛谷洛谷月赛4月月赛Round 1/2