[20181125]四校联考

商人(business)

[题意]

n个节点的树上有m条简单路径,对于每条路径,求出有多少条路径与它相交。

我的做法看上去就很low。

[题解]

  • 开两个树状数组,分别记下lca在当前点之上的和在当前点之下的。

  • 对于后者,用dfs序就可以计算子树内的value和

  • 对于前者,我们把询问按照lca的dep值排序来做

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define swap(x,y) (x^=y^=x^=y) 
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<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define MN 200005
int n,m,hr[MN],en;
struct ed{int to,nex;}e[MN<<1];
inline void ins(int f,int t)
{
	e[++en]=(ed){t,hr[f]};hr[f]=en;
	e[++en]=(ed){f,hr[t]};hr[t]=en;
}
struct Ques{int a,b,id,LCA;}q[MN];
int ans[MN];
int mx[MN],siz[MN],dep[MN],fa[MN],top[MN],dfn[MN],r[MN],dind;
inline bool cmp(Ques o,Ques oo){return dep[o.LCA]<dep[oo.LCA];}
void dfs1(int x,int f,int d)
{
	register int i;
	fa[x]=f;dep[x]=d;siz[x]=1;
	for(i=hr[x];i;i=e[i].nex)if(f^e[i].to)
	{
		dfs1(e[i].to,x,d+1);siz[x]+=siz[e[i].to];
		siz[e[i].to]>siz[mx[x]]?mx[x]=e[i].to:0;
	}
}
void dfs2(int x,int f,int tp)
{
	top[x]=tp;dfn[x]=++dind;
	if(mx[x]) dfs2(mx[x],x,tp);
	register int i;
	for(i=hr[x];i;i=e[i].nex)
		if((f^e[i].to)&&(e[i].to^mx[x]))
			dfs2(e[i].to,x,e[i].to);
	r[x]=dind;
}
int lca(int x,int y)
{
	for(;top[x]^top[y];)dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
	return dep[x]<dep[y]?x:y;
}
/*
namespace mini
{
	
	inline bool Cross(int x,int y)
	{
		int lcax=q[x].LCA,lcay=q[y].LCA;
		if(dep[lcax]>dep[lcay]) swap(x,y),swap(lcax,lcay);
		return lcay==lca(lcay,q[x].a)||lcay==lca(lcay,q[x].b); 
	}
	void solve()
	{
		register int i,j;
		for(i=1;i<n;i++) j=read(),ins(j,read());dfs1(1,0,1);dfs2(1,0,1);
		for(i=1;i<=m;i++) q[i].a=read(),q[i].b=read(),q[i].a>q[i].b?swap(q[i].a,q[i].b):0;
		for(i=1;i<=m;i++) q[i].LCA=lca(q[i].a,q[i].b);
		for(i=1;i<m;i++)for(j=i+1;j<=m;j++) if(Cross(i,j)) ans[i]++,ans[j]++;
		for(i=1;i<=m;i++) printf("%d\n",ans[i]);
		return;
	}
}*/
namespace Normal
{
	#define lowbit(x) (x&(-x))
	int ht[MN],tt[MN];
	inline void hC(int x,int val){for(;x<=n;x+=lowbit(x)) ht[x]+=val;}
	inline void tC(int x,int val){for(;x<=n;x+=lowbit(x)) tt[x]+=val;}
	inline int hG(int x){int res=0;for(;x;x-=lowbit(x)) res+=ht[x];return res;}
	inline int tG(int x){int res=0;for(;x;x-=lowbit(x)) res+=tt[x];return res;}
	int llca(int x,int y)
    {
    	int res=0;
		for(;top[x]^top[y];)
		{
			if(dep[top[x]]>dep[top[y]])
			{
				res+=tG(dfn[x])-tG(dfn[top[x]]-1);
				x=fa[top[x]];
			}
			else
			{
				res+=tG(dfn[y])-tG(dfn[top[y]]-1);
				y=fa[top[y]];
			}
		}
		if(dep[x]>dep[y]) res+=tG(dfn[x])-tG(dfn[y]-1);
		else res+=tG(dfn[y])-tG(dfn[x]-1);
		return res;
	}
	void solve()
	{
		register int i,j;
		for(i=1;i<n;i++) j=read(),ins(j,read());
		dfs1(1,0,1);dfs2(1,0,1);
		for(i=1;i<=m;i++) q[i].a=read(),q[i].b=read(),q[i].a>q[i].b?swap(q[i].a,q[i].b):0;
		for(i=1;i<=m;i++) q[i].LCA=lca(q[i].a,q[i].b),q[i].id=i,tC(dfn[q[i].LCA],1);
		std::sort(q+1,q+m+1,cmp);
		for(i=1,j=1;i<=m;i++)
		{
			while(dep[q[j].LCA]<dep[q[i].LCA]) hC(dfn[q[j].a],1),hC(dfn[q[j].b],1),j++;
			ans[q[i].id]=hG(r[q[i].LCA])-hG(dfn[q[i].LCA]-1);
			ans[q[i].id]+=llca(q[i].a,q[i].b);
		}
		for(i=1;i<=m;i++) printf("%d\n",ans[i]-1);
	}
} 
int main()
{
	freopen("business.in","r",stdin);
	freopen("business.out","w",stdout);
	n=read();m=read();
	Normal::solve();
	return 0;
}

当然,有清楚得多的做法:

  • 如果两条链相交,他们相交的点数减相交的边数等于 1,否则等于 0。对每条链将其链上的点加点权 1,边加边权-1,那么对一条链求链上权值和就可以知道这条链与多少条链有交,用求 LCA 和树上差分等技巧优化加权值以及查权值和即可,时间复杂度𝑂(𝑛)𝑂(𝑛log𝑛)




栅栏(barrier) 

[题意]

给定一个数组,求有多少个排列\(P_i\) 满足

\[\sum_{i=2}^{n}|𝑎_{p_i}−𝑎_{p_{i-1}}|\leq l \]

[题解]

  • 我们考虑一开始排列所有的数都为0,从小到大的将\(a_i\)扔进去,每次我们把所有还未赋值的都赋为\(a_i\)
  • 这样,当我们更新到\(a_{i+1}\)时,原式中的相邻差之和增大 (a[i+1]-a[i])(2*k+2-l),其中,k表示的是当前排列中有k对相邻数之间还要插入数字,l表示序列的两端中有几个还要插入数字,\(l\leq 2\)
  • 于是我们设计状态f[i][j][k][l]表示,前i个数已经加入,当前的相邻差之和为j,k、l的定义如上
  • 状态的转移略显麻烦,主要是要考虑到所有的情况

时间复杂度\(𝑂(𝑛^2𝑙)\)



#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define swap(x,y) (x^=y^=x^=y)
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<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define MN 105
#define MM 1005
#define mod 1000000007
int n,lm,a[MN];
ll f[MN][MM][MN][3],ans;
inline void add(ll &x,ll y){x+=y;x%=mod;}
int main()
{
	freopen("barrier.in","r",stdin);
	freopen("barrier.out","w",stdout);
	n=read();lm=read();
	register int i,j,k,l;
	for(i=1;i<=n;++i) a[i]=read();
	std::sort(a+1,a+n+1);
	if(n==1) return 0*puts("1");
	f[1][0][0][0]=1;f[1][0][0][1]=2;
	for(i=1;i<n;++i)for(j=0;j<=lm;++j)for(k=0;k<=i;++k)for(l=0;l<3;++l)if(f[i][j][k][l]){
		if(k==0&&l==2) continue;
		int J=j+(a[i+1]-a[i])*(k*2+2-l);if(J>lm) continue;
		if(l<2)add(f[i+1][J][k][l+1],f[i][j][k][l]*(2-l));
		if(l<2)add(f[i+1][J][k+1][l+1],f[i][j][k][l]*(2-l));
		add(f[i+1][J][k+1][l],f[i][j][k][l]*(k+2-l));
		if(k>0)add(f[i+1][J][k-1][l],f[i][j][k][l]*(k));
		add(f[i+1][J][k][l],f[i][j][k][l]*(k*2+2-l));
	}
	for(i=0;i<=lm;++i) add(ans,f[n][i][0][2]);
	return 0*printf("%d",ans);
}




地铁(subway) 

[问题描述]

小 R 在 X 城旅游,X 城有编号为1到𝑛的𝑛个地铁站,还有编号为1到𝑚的𝑚条地铁线,其中𝑖号地铁线从\(𝑣_{𝑖,0}\)出发依次经过 \(𝑣_{𝑖,1},𝑣_{𝑖,2},…,𝑣_{𝑖,𝑠𝑖}\),𝑖号线从\(𝑣_{𝑖,𝑗−1}\)前往 \(𝑣_{i,𝑗}\)需要花费\(𝑡_{𝑖,𝑗}\)的时间,线路都是单向的。

小 R 现在要从1号站乘地铁前往𝑛号站,他可以任意换乘。他希望自己乘坐地铁的时间之和尽量小,并且在此基础上,他不希望换乘地太频繁,他定义每次乘坐地铁的舒适度为上车到下车所经过时间的平方,他希望舒适度之和尽量大。

[输入格式]

第一行两个正整数𝑛,𝑚

接下来𝑚行,每行先给出一个正整数𝑠𝑖,接下来2𝑠𝑖 + 1个正 整数,依次分别为\(𝑣_{𝑖,0},𝑡_{𝑖,1},𝑣_{𝑖,1},𝑡_{𝑖,2},𝑣_{𝑖,2},…,𝑡_{𝑖,𝑠𝑖},𝑣_{𝑖,𝑠𝑖}\)

保证从1号站出发能到达𝑛号站。

[输出格式]

输出一行两个整数,分别表示最小乘坐时间以及在乘坐时间+最小时最大的舒适度之和。

[题解]

  • 首先是最小乘坐时间,把地铁线路拆成一段一段建图跑最短路即可。

    发现std中是用线段树求最短路的,感觉太巨了

  • 对于第二问,我们考虑 DP,用𝑓[𝑖]表示从1走到𝑖路程最短下最大的舒适度之和,用𝑑𝑖𝑠[𝑖]表示 1 到𝑖的最短路,\(𝑇_{𝑖,𝑗}\)表示\(𝑡_{𝑖,𝑗}\)的前缀和,那么如果\(𝑇_{𝑖,𝑗}-T_{𝑖,𝑘} = 𝑑𝑖𝑠[𝑣_{i,𝑗}] − 𝑑𝑖𝑠[𝑣_{𝑖,𝑘}]\), 我们就可以用\(𝑓[𝑣_{𝑖,𝑘}] + (𝑇_{𝑖,𝑗} − 𝑇_{𝑖,𝑘})^2\)更新⁡\(𝑓[𝑣_{𝑖,𝑗}]\),我们可以按\(𝑑𝑖𝑠[𝑖]\)从小到大的顺序依次计算出每个\(𝑓[𝑖]\)。不难注意到,这个转移式可以用斜率优化来加速计算,我们在 DP 的同时对每一段可能转移过来的线路开一个队列维护斜率优化需要的信息即可。



#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
char B[1<<25],*S=B;
inline int read()
{
	int x;char c;
	while((c=*S++)<'0'||c>'9');
	for(x=c-'0';(c=*S++)>='0'&&c<='9';)x=x*10+c-'0';
	return x;
}
#define MN 2000005
#define inf (2000000000)
#define N 1048576
std::vector<int> v[MN],w[MN],u[MN],q[MN];
std::vector<std::pair<int,int> >p[MN];
int d[MN],qn;
ll f[MN];
struct node{int x,f;}t[MN<<1];
inline node min(const node&o,const node&oo){return o.x<oo.x?o:oo;}
inline void rw(int k,int x){for(t[k+=N].x=x;k>>=1;)t[k]=min(t[k<<1],t[k<<1|1]);}
inline ll sqr(ll x){return x*x;}
inline double cal(int x,int y){return (double)(sqr(d[x])+f[x]-sqr(d[y])-f[y])/(d[x]-d[y]);}
int main()
{
	freopen("subway.in","r",stdin);
	freopen("subway.out","w",stdout);
	register int n,m,i,j,x,y,z,k;
	fread(B,1,1<<25,stdin);
	n=read(),m=read();
	for(i=1;i<=m;++i)
	{
		j=read();v[i].push_back(read());
		while(j--) w[i].push_back(read()),v[i].push_back(read());
		for(j=0;j<v[i].size();++j)
			p[v[i][j]].push_back(std::make_pair(i,j));
		u[i].resize(v[i].size());
	}
	for(i=1;i<N+N;++i) t[i].x=inf;
	for(i=1;i<=n;++i) d[t[i+N].f=i]=inf;
	for(rw(1,d[1]=0);t[1].x!=inf;)
	{
		rw(x=t[1].f,inf);
		for(i=0;i<p[x].size();++i)
		{
			y=p[x][i].first;z=p[x][i].second;
			k=u[y][z]=z&&d[v[y][z-1]]+w[y][z-1]==d[x]?u[y][z-1]:++qn;
			while(q[k].size()>1&&cal(q[k].back(),q[k][q[k].size()-2])<2*d[x])q[k].pop_back();
			if(q[k].size())f[x]=max(f[x],f[q[k].back()]+sqr(d[x]-d[q[k].back()]));
		}
		for(i=0;i<p[x].size();++i)
		{
			y=p[x][i].first;z=p[x][i].second;k=u[y][z];
			while(q[k].size()>1&&cal(x,q[k].back())>cal(q[k].back(),q[k][q[k].size()-2]))q[k].pop_back();
			q[k].push_back(x);
			if(z+1<v[y].size()&&d[x]+w[y][z]<d[v[y][z+1]])
				rw(v[y][z+1],d[v[y][z+1]]=d[x]+w[y][z]);
		}
	}
	printf("%d %lld",d[n],f[n]);
}




Blog来自PaperCloud,未经允许,请勿转载,TKS!

posted @ 2018-11-27 00:02  PaperCloud  阅读(248)  评论(0编辑  收藏  举报