北京Day 14

今天是传说中的“ACMOI”(自己起的名字)赛制,只能提交一次,所有测试点捆绑在一起,不AC记0分。

于是乎我就光荣地只拿了一道题。

T1[POI2017]Turysta

题目大意:给出 n 个点的有向图,任意两个点之间有且仅一条有向边。 对于每个点 v,求出从 v 出发的一条经过点数最多,且没有重复经过同一个点两次 及两次以上的简单路径。n<=2000。

题解自己搜一下。

T2

题目大意:n个点,m次操作,(a,b)(c,d)表示如果a <= x <= b, c <= y <= d,那么在 x 和 y 之间添加一条无向边。最后求单源最短路。n<=500000,m<=100000。

题解:线段树优化建图,建出两颗线段树,一颗儿子向父亲连边,一颗父亲向儿子连边,后者向前者对应区间连边,每次操作新建一个节点,前者对应区间向新节点连边,新节点向后者对应区间连边。最后一种边权值为1,其余为0.BFS单源最短路。

其实还可以用动态开点优化。

T2AC代码(未动态开点)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<set>
#include<bitset>
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
	return x*f;
}
int n,m,P,tot,cnt;
vector<int> now1,now2;
struct node{int to,cost;};
vector<node>g[6000010];
int t1[2000010],t2[2000010],pos[500010],dis[6000010];
void build1(int k,int l,int r)
{
	t1[k]=++tot;
	if(l==r){pos[l]=tot;return;}
	int mid=(l+r)>>1;
	build1(k<<1,l,mid);build1(k<<1|1,mid+1,r);
	g[t1[k<<1]].push_back((node){t1[k],0});g[t1[k<<1|1]].push_back((node){t1[k],0});
}
void build2(int k,int l,int r)
{
	t2[k]=++tot;g[tot].push_back((node){tot-cnt,0});
	if(l==r){return;}
	int mid=(l+r)>>1;
	build2(k<<1,l,mid);build2(k<<1|1,mid+1,r);
	g[t2[k]].push_back((node){t2[k<<1],0});g[t2[k]].push_back((node){t2[k<<1|1],0});
}
void ask1(int k,int l,int r,int L,int R)
{
	if(l>=L&&r<=R){now1.push_back(t1[k]);return;}
	int mid=(l+r)>>1;
	if(R<=mid)ask1(k<<1,l,mid,L,R);
	else if(L>mid)ask1(k<<1|1,mid+1,r,L,R);
	else ask1(k<<1,l,mid,L,R),ask1(k<<1|1,mid+1,r,L,R);
}
void ask2(int k,int l,int r,int L,int R)
{
	if(l>=L&&r<=R){now2.push_back(t2[k]);return;}
	int mid=(l+r)>>1;
	if(R<=mid)ask2(k<<1,l,mid,L,R);
	else if(L>mid)ask2(k<<1|1,mid+1,r,L,R);
	else ask2(k<<1,l,mid,L,R),ask2(k<<1|1,mid+1,r,L,R);
}
deque<node>q;
inline void bfs(int s)
{
	register node x;
	register int i,sz,u,v,co;
	memset(dis,127,sizeof(dis));
	q.push_front((node){s,0});
	while(!q.empty())
	{
	x=q.front();q.pop_front();
	u=x.to;co=x.cost;sz=g[u].size();//cout<<u<<endl;
	if(dis[u]<=co)continue;
	dis[u]=co;
	for(i=0;i<sz;++i)
	{
	v=g[u][i].to;
	if(g[u][i].cost){q.push_back((node){v,co+1});}
	else {q.push_front((node){v,co});}
	}
	}
}
int main()
{
	register int i,j,k,a,b,c,d,sz1,sz2;
	n=re_ad();m=re_ad();P=re_ad();
	build1(1,1,n);cnt=tot;build2(1,1,n);
	for(i=1;i<=m;++i)
	{a=re_ad();b=re_ad();c=re_ad();d=re_ad();
	now1.clear();now2.clear();
	ask1(1,1,n,a,b);ask2(1,1,n,c,d);
	sz1=now1.size();sz2=now2.size();
	++tot;
	for(j=0;j<sz1;++j)g[now1[j]].push_back((node){tot,1});
	for(k=0;k<sz2;++k)g[tot].push_back((node){now2[k],0});
	now1.clear();now2.clear();
	ask1(1,1,n,c,d);ask2(1,1,n,a,b);
	sz1=now1.size();sz2=now2.size();
	++tot;
	for(j=0;j<sz1;++j)g[now1[j]].push_back((node){tot,1});
	for(k=0;k<sz2;++k)g[tot].push_back((node){now2[k],0});
	}
	bfs(pos[P]);
	for(i=1;i<=n;++i)printf("%d\n",dis[pos[i]]);
}

T3

题目大意:给定一张有向图,其中一些边的权值已知,另一些的权值为x(未知的正整数),多组询问,每次询问s到t的最短路径长度可能的数目和它们的总和。n<=500,m<=10000,q<=10。

题解:spfa求出s到t的最短路所有的kx+b形式,李超树合并由于数据较小,n^2暴力枚举交点统计答案即可。(其实维护一个凸包就可以了。)

T3AC代码

​
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<set>
#include<bitset>
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
	return x*f;
}
int n,m,Q,inf;
long long ans=0,an[510];
bool ckjlx[510][510],vis[510];
int ptlx[510][510];
struct line{int k,b;long long f(int x){return k*x+b;}};
bool cmp(line x,line y){return x.k<y.k;}
struct node{int to,cost;};
vector<line>nu[510];
vector<int>ts[510];
vector<node>zc[510];
queue<int> q;
inline bool gx(int x,int y,int num)
{
	bool fla=false;
	register int i,j,k,b;
	if(num)
	{
	for(i=0;i<nu[x].size();++i)
	{
	k=nu[x][i].k;b=nu[x][i].b+num;
	for(j=0;j<nu[y].size();++j)
	{
	if(nu[y][j].k<=k&&nu[y][j].b<=b){break;}
	if(nu[y][j].k==k){if(nu[y][j].b>b)nu[y][j].b=b,fla=true;break;}
	}
	if(j==nu[y].size()){nu[y].push_back((line){k,b});fla=true;}
	}
	}
	else
	{
	for(i=0;i<nu[x].size();++i)
	{
	k=nu[x][i].k+1;b=nu[x][i].b;
	for(j=0;j<nu[y].size();++j)
	{
	if(nu[y][j].k<=k&&nu[y][j].b<=b){break;}
	if(nu[y][j].k==k){if(nu[y][j].b>b)nu[y][j].b=b,fla=true;break;}
	}
	if(j==nu[y].size()){nu[y].push_back((line){k,b});fla=true;}
	}
	}
	return fla;
}
inline void spfa(int s)
{
	register int i,x,sz,v;
	for(i=1;i<=n;++i)nu[i].clear();
	memset(vis,0,sizeof(vis));
	nu[s].push_back((line){0,0});vis[s]=true;
	q.push(s);
	while(!q.empty())
	{
	x=q.front();q.pop();vis[x]=0;
	sz=zc[x].size();
	for(i=0;i<sz;++i)
	{
	v=zc[x][i].to;
	if(gx(x,v,zc[x][i].cost))
	{if(!vis[v])q.push(v),vis[v]=true;}
	}
	sz=ts[x].size();
	for(i=0;i<sz;++i)
	{
	v=ts[x][i];
	if(gx(x,v,0))
	{if(!vis[v])q.push(v),vis[v]=true;}
	}
	}
}
void solve(int s,int t)
{
	spfa(s);
	if(nu[t].size()==0){cout<<0<<" "<<0<<endl;return;}
	ans=0;memset(an,-1,sizeof(an));
	register int i,sz=nu[t].size(),mi=inf,ma,x=1,jd,jd2,ans2=0,las=0;
	sort(nu[t].begin(),nu[t].end(),cmp);
	for(i=0;i<sz;++i){an[nu[t][i].k]=nu[t][i].b;}
	if(an[0]==-1){cout<<"inf"<<endl;return;}
	if(sz==1){cout<<1<<" "<<an[0]<<endl;return ;}
	mi=sz-1;
	while(mi!=0)
	{
	jd=inf;ma=mi;las=mi;
	for(i=mi-1;i>=0;--i){jd2=(nu[t][i].b-nu[t][mi].b)/(nu[t][mi].k-nu[t][i].k);if(jd2<jd)ma=i,jd=jd2;}
	ans2+=jd-x+1;ans+=(nu[t][mi].f(x)+nu[t][mi].f(jd))*(jd-x+1)/2;
	mi=ma;x=jd+1;
	}
	if(nu[t][las].f(x-1)!=nu[t][0].b)ans+=nu[t][0].b,ans2++;
	cout<<ans2<<" "<<ans<<endl;
}
int main()
{
	register int i,j,l,x,y,z;
	register char op[10];
	n=re_ad();m=re_ad();
	memset(ptlx,127,sizeof(ptlx));inf=ptlx[1][1];
	for(i=1;i<=m;++i)
	{
	x=re_ad();y=re_ad();scanf("%s",op+1);
	if(op[1]=='x')ckjlx[x][y]=true;
	else
	{
	l=strlen(op+1);z=0;for(j=1;j<=l;++j)z=z*10+op[j]-48;
	ptlx[x][y]=min(ptlx[x][y],z);
	}
	}
	for(i=1;i<=n;++i)
	for(j=1;j<=n;++j)
	{
	if(ptlx[i][j]!=inf)zc[i].push_back((node){j,ptlx[i][j]});
	if(ckjlx[i][j])ts[i].push_back(j);
	}
	Q=re_ad();
	while(Q--)
	{
	x=re_ad();y=re_ad();solve(x,y);
	}
}

​

T4

题目大意:给定序列 A,序列中的每一项 Ai 有删除代价 Bi 和附加属性 Ci。删除若干项,使得A的最长上升子序列长度减少至少 1,且付出的代价之和最小,并输出方案。 如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。n<=700

题解:

用dp跑出lis。对于每一个lis,我们都要删掉至少一个数,考虑最小割。对于所有的i<j,a[i]<a[j],f[i]+1==f[j] 的点,都i−>j连边,然后求出最小割。 

  考虑输出方案。用退流的思想。我们先随便求出一个最小割,然后考虑什么样的边能成为割边。第一个必要条件就是满流,并且如果我们把这条边断掉,那么这条路径上的其他边就没有选的必要了。这样的话如果我们选了一条边u−>v,那么我们就需要把s−>u和v−>t的路径上的流量退掉。要求字典序最小,就从小到大枚举边,然后判断这条边是否为割边:满流并且不存在增广路。如果符合就选择它,并且把这条路径上的流量退掉。
退流:T向v跑一边最大流,u向S跑一边最大流,这条边流量清零

 

T5

题目大意:有 n 个杯子排成一行,编号为 1, 2, . . . , n,其中某些杯子底下藏有一个小球。花费 ci, j 元,可以知道杯子 i, i + 1, . . . , j 底下藏有球的总数的奇偶性。 采取最优的询问策略,至少需要花费多少元,才能保证猜出哪些杯子底下藏 着球?<=2000。

题解:其实直接跑最小生成树就可以……

T6

题目大意:给出一个 N 个点 M 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 1 到点 N 的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权。n<=100000,m<=200000

题解:

首先考虑暴力点的建图:

把每条无向边拆成两条有向边,把每条边看成一个点,对于两条边a->b,b->c,在这两条边之间连有向边,边权为这两条边的权值的较大值。新建源点S,汇点T,S向所有从1连出去的边连边,所有指向n的边向T连边。求S->T的最短路即可.

考虑优化一下,类似网络流中补流思想,利用差值来建边。

依然把每条边x-y拆成x->y,y->x。枚举每个中转点x,将x的出边按权值排序,x的每条入边向对应的出边连该边权值的边,x的每条出边向第一个比它大的出边连两边权差值的边,x的每条出边向第一个比它小的出边连权值为0的边。新建源汇S,T,S向每条1的出边连权值为该边边权的边,每条n的入边向T连该边权值的边,跑S->T的最短路即可。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值