20241021比赛总结

T1 岛屿

https://www.gxyzoj.com/d/hzoj/p/4177

显然每个点只增加了一条边,最终每个点的度数都为2,所以最终必然是很多个环,连边的过程中,也必然是一些链和一些环

由题,蓝同色链的个数和红同色链的个数相等,所以设f(a,b)为a条红同色链,b条异色链的期望

考虑先处理异色链:

红红连红蓝为红红,异色链-1,即f(a,b1)

蓝蓝连红蓝为蓝蓝,异色链-1,即f(a,b1)

红蓝连红蓝为红蓝(不是自己),异色链-1,即f(a,b1)

红蓝连自己,此时构成环,联通快数量+1,即f(a,b1)+1

剩余的同色链相连为红蓝,即f(a1,b+1)

所以:

f(a,b)={12a+b(f(a,b1)+1)+2a+b12a+bf(a,b1),b>0f(a1,b+1),b=0

既:

f(a,b)={12a+b+f(a,b1),b>0f(a1,0)+12a+1,b=0

f(0,0)=0,整理得:

f(x,y)=b=1y12x+b+a=1x12a+1

代码:

#include<cstdio>
using namespace std;
int x,y,n;
double ans;
int main()
{
	freopen("island.in","r",stdin);
	freopen("island.out","w",stdout);
	scanf("%d%d",&x,&y);
	n=2*x+y;
	for(int i=1;i<=y;i++)
	{
		ans+=1.0/(1.0*(2*x+i));
	}
	for(int i=1;i<=x;i++)
	{
		ans+=1.0/(1.0*(i*2-1));
	}
	printf("%.10lf",ans);
	return 0;
}

T2 最短路

https://www.gxyzoj.com/d/hzoj/p/4178

可以先求出1到所有点的最短路长度,然后建最短路树,即让树上所有点到根的距离就是他到1的最短路

此时,去掉最后一条边就是取掉边u>fa,此时考虑非树边的贡献

如图,考虑边(u,v),在其上取一点k(不取lca)

可以发现,因为是无向边,所以u,v,k,1在一个环上,考虑(u,v)对k的贡献,显然是

(1>u)+(u>v)+(v>k)=disu+disv+wu,vdisk

所以可以树剖+线段树

考虑优化,因为disu+disv+wu,v是定值,所以可以按该值排序,然后用并查集记录下一个未被更新的点,依次跳父亲更新即可

#include<cstdio>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
int n,m,head[100005],edgenum;
int f[100005][19],dep[100005];
ll dis[100005],ans[100005];
int pre[100005];
struct edge{
	int to,nxt;
	ll val;
}e[400005];
struct Edge{
	int u,v;
	ll val;
}a[200005];
struct node{
	int x,pre;
	ll val;
	bool operator < (const node &x)const{
		return val>x.val;
	}
};
priority_queue<node> q;
bool cmp(Edge x,Edge y)
{
	return x.val<y.val;
}
void add_edge(int u,int v,ll w)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	e[edgenum].val=w;
	head[u]=edgenum;
}
void dijkstra()
{
	q.push((node){1,1,0});
	while(!q.empty())
	{
		int u=q.top().x,p=q.top().pre;
		ll val=q.top().val;
		q.pop();
		if(!pre[u])
		{
			pre[u]=p,dis[u]=val;
			for(int i=head[u];i;i=e[i].nxt)
			{
				int v=e[i].to;
				if(!pre[v])
				{
					q.push((node){v,u,val+e[i].val});
				}
			}
		}
	}
}
void dfs(int u,int fa)
{
	dep[u]=dep[fa]+1;
	f[u][0]=fa;
	for(int i=1;i<=18;i++)
	{
		f[u][i]=f[f[u][i-1]][i-1];
	}
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=18;i>=0;i--)
	{
		if(dep[f[x][i]]>=dep[y])
		{
			x=f[x][i];
		}
		if(x==y) return x;
	}
	for(int i=18;i>=0;i--)
	{
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
}
int fa[100005];
int find(int x)
{
	if(fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}
void solve(int u,int ed,ll val)
{
	while(dep[u]>dep[ed])
	{
		if(u==ed) return;
		u=find(u);
		if(dep[u]<=dep[ed]) return;
		ans[u]=val;
		fa[u]=find(ed);
		u=pre[u];
	}
}
int main()
{
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		add_edge(u,v,w);
		add_edge(v,u,w);
		a[i]=(Edge){u,v,w};
	}
	dijkstra();
//	printf("1");
	for(int i=1;i<=n;i++) head[i]=0,fa[i]=i;
	edgenum=0;
	for(int i=2;i<=n;i++)
	{
		add_edge(i,pre[i],dis[i]-dis[pre[i]]);
		add_edge(pre[i],i,dis[i]-dis[pre[i]]);
	}
	dfs(1,0);
	for(int i=1;i<=m;i++)
	{
		int lc=lca(a[i].u,a[i].v);
		if(lc==a[i].u||a[i].v==lc)
		{
			for(int j=head[a[i].u];j;j=e[j].nxt)
			{
				if(a[i].v==e[j].to&&a[i].val==e[j].val)
				{
					a[i].val=1e18;
				}
			}
		}
	}
	for(int i=1;i<=m;i++)
	{
		a[i].val+=dis[a[i].u]+dis[a[i].v];
	}
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		int lc=lca(a[i].u,a[i].v);
	//	printf("%d %d %d\n",a[i].u,a[i].v,lc);
		if(a[i].val>=1e18) continue;
		solve(a[i].u,lc,a[i].val);
		solve(a[i].v,lc,a[i].val);
	}
	for(int i=2;i<=n;i++)
	{
		if(ans[i]==0) printf("-1\n");
		else printf("%lld\n",ans[i]-dis[i]);
	}
	return 0;
}

T3 列表

https://www.gxyzoj.com/d/hzoj/p/4179

部分分记得return 0,注意部分分的范围,不要忘了等于的情况

显然有如下性质:

性质1:对于区间[n+1i,n+1+i]中至少选了i+1

性质2:对于区间[1,n+1i][n+1+i,n2+1]中至多取了ni

将原序列沿n+1对折,以样例为例,就是:

4 7 3 6
5 2 1

计算每一列的取的个数,求前缀和,显然sumin(ni)

所以sumii0

线段树区间修改,单点查询+双指针即可

代码:

#include<cstdio>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
using namespace std;
int n,a[400005],b[400005];
struct seg_tr{
	int l,r,mx,lazy;
}tr[2000005];
void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r;
	if(l==r)
	{
		tr[id].mx=-l;
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	tr[id].mx=max(tr[lid].mx,tr[rid].mx);
}
void pushdown(int id)
{
	if(tr[id].lazy)
	{
		tr[lid].lazy+=tr[id].lazy;
		tr[rid].lazy+=tr[id].lazy;
		tr[lid].mx+=tr[id].lazy;
		tr[rid].mx+=tr[id].lazy;
		tr[id].lazy=0;
	}
}
void update(int id,int l,int r,int val)
{
	if(tr[id].l==l&&r==tr[id].r)
	{
		tr[id].mx+=val;
		tr[id].lazy+=val;
		return;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) update(lid,l,r,val);
	else if(l>mid) update(rid,l,r,val);
	else update(lid,l,mid,val),update(rid,mid+1,r,val);
	tr[id].mx=max(tr[lid].mx,tr[rid].mx);
}
int main()
{
	freopen("list.in","r",stdin);
	freopen("list.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=1+2*n;i++)
	{
		scanf("%d",&a[i]);
		b[a[i]]=i;
	}
	build(1,1,n+1);
	int ans=1;
	for(int i=1;i<=n*2+1;i++)
	{
		b[i]=min(2*n+2-b[i],b[i]);
	}
	for(int i=1,j=1;i<=n*2+1;i++)
	{
		update(1,b[i],n+1,1);
		while(tr[1].mx>0)
		{
			update(1,b[j],n+1,-1);
			j++;
		}
		ans=max(ans,i-j+1);
	}
	printf("%d",ans);
	return  0;
}
posted @   wangsiqi2010916  阅读(35)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示