2025“钉耙编程”中国大学生算法设计春季联赛(7)

木柜子组乐队

#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int a,b,c,d,e;
		cin>>a>>b>>c>>d>>e;
		cout<<a*b*c*(d*(d-1)/2)+a*b*c*d*e<<"\n";
	}
	return 0;
}

森林迷宫

  • 记得别再用两个vector存边了,这样写就好了:
a[u].emplace_back(v,w);
a[v].emplace_back(u,w);
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef tuple<int,int,int> edge;
vector<edge>a[100005];
int d[100005],fa[100005],f[100005];
bool vis[100005];
void dfs(int u)
{
	for(auto [v,w,c]:a[u])
	{
		if(v!=fa[u])
		{
			d[v]=d[u]+w;
			fa[v]=u;
			dfs(v);
		}
	}
}
void dp(int u)
{
	f[u]=0;
	for(auto [v,w,c]:a[u])
	{
		if(!vis[v]&&v!=fa[u])
		{
			dp(v);
			if(f[v]+c>0)
			{
				f[u]=f[u]+f[v]+c;
			}
		}
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			a[i].clear();
			vis[i]=false;
		}
		for(int i=1;i<n;i++)
		{
			int u,v,p,q;
			cin>>u>>v>>p>>q;
			a[u].emplace_back(v,p,p+q);
			a[v].emplace_back(u,q,q+p);
		}
		int s,t;
		cin>>s>>t;
		d[s]=fa[s]=0;
		dfs(s);
		int ans=d[t];
		vector<int>path;
		while(t!=s)
		{
			path.push_back(t);
			vis[t]=true;
			t=fa[t];
		}
		path.push_back(s);
		vis[s]=true;
		reverse(path.begin(),path.end());
		for(int x:path)
		{
			dp(x);
			ans+=f[x];
		}
		cout<<ans<<"\n";
	}
	return 0;
}

最早连续串

  • 分别维护0和1就好了。你不应该拖到三个半小时的时候才过掉这题的
#include <bits/stdc++.h>
using namespace std;
int a[500005];
struct t1
{
	int l,r,bj;
	int lc[2],rc[2],maxc[2];
	#define len(x) t[x].r-t[x].l+1
}t[2000005];
void build(int p,int l,int r)
{
	t[p].l=l;
	t[p].r=r;
	t[p].bj=-1;
	if(l==r)
	{
		t[p].lc[a[l]]=t[p].rc[a[l]]=t[p].maxc[a[l]]=1;
		t[p].lc[a[l]^1]=t[p].rc[a[l]^1]=t[p].maxc[a[l]^1]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	for(int i=0;i<2;i++)
	{
		t[p].lc[i]=t[p*2].lc[i]+(t[p*2].lc[i]==len(p*2))*t[p*2+1].lc[i];
		t[p].rc[i]=t[p*2+1].rc[i]+(t[p*2+1].rc[i]==len(p*2+1))*t[p*2].rc[i];
		t[p].maxc[i]=max({t[p*2].maxc[i],t[p*2+1].maxc[i],t[p*2].rc[i]+t[p*2+1].lc[i]});
	}
}
void cl(int p,int v)
{
	t[p].lc[v]=len(p);
	t[p].rc[v]=len(p);
	t[p].maxc[v]=len(p);
	t[p].lc[v^1]=0;
	t[p].rc[v^1]=0;
	t[p].maxc[v^1]=0;
}
void spread(int p)
{
	if(t[p].bj!=-1)
	{
		int x=t[p].bj;
		t[p*2].bj=t[p].bj;
		t[p*2+1].bj=t[p].bj;
		cl(p*2,x);
		cl(p*2+1,x);
		t[p].bj=-1;
	}
}
void change(int p,int l,int r,int v)
{
	if(l<=t[p].l&&r>=t[p].r)
	{
		cl(p,v);
		t[p].bj=v;
		return;
	}
	spread(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)
	{
		change(p*2,l,r,v);
	}
	if(r>mid)
	{
		change(p*2+1,l,r,v);
	}
	for(int i=0;i<2;i++)
	{
		t[p].lc[i]=t[p*2].lc[i]+(t[p*2].lc[i]==len(p*2))*t[p*2+1].lc[i];
		t[p].rc[i]=t[p*2+1].rc[i]+(t[p*2+1].rc[i]==len(p*2+1))*t[p*2].rc[i];
		t[p].maxc[i]=max({t[p*2].maxc[i],t[p*2+1].maxc[i],t[p*2].rc[i]+t[p*2+1].lc[i]});
	}
}
int ask(int p,int x,int k)
{
	if(t[p].l==t[p].r)
	{
		return t[p].l;
	}
	spread(p);
	if(t[p*2].maxc[x]>=k)
	{
		return ask(p*2,x,k);
	}
	else if(t[p*2].rc[x]+t[p*2+1].lc[x]>=k)
	{
		return ((t[p].l+t[p].r)>>1)-t[p*2].rc[x]+1;
	}
	else
	{
		return ask(p*2+1,x,k);
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		string s;
		cin>>s;
		for(int i=0;i<s.size();i++)
		{
			a[i+1]=s[i]-'0';
		}
		build(1,1,s.size());
		int q;
		cin>>q;
		while(q--)
		{
			int op,k;
			cin>>op>>k;
			if(t[1].maxc[op]<k)
			{
				cout<<"-1\n";
			}
			else
			{
				int p=ask(1,op,k);
				cout<<p<<"\n";
				change(1,p,p+k-1,op^1);
			}
		}
	}
	return 0;
}

未来城市

  • 对费用流有了更深刻的理解:如果原图不存在负圈,SSP算法可以保证迭代过程中的新图也不会出现负圈,但是如果你要手动跑费用流,那得到的图有没有负圈就不一定了
  • 网络流算法的确可以更快地解决二分图最大匹配问题,但此时得到的解就不受你的控制了
  • 本题的关键点在于要想到在\(a_i\)\(b_i\)之间连边,同一个连通块内的点之间都可以相互替代
  • 同样试图用二分图最大匹配“乱搞”,你看别人就能想到限制每个点至多访问100次,你就没能想到

  • 等一下,突然发现你电脑上存储的最后一个版本提交是能通过的???只是赛场上你自己都不相信能过所以干脆都没交???
  • 大概是这一行“乱搞”代码真的起了作用,不知道该说什么好……:
if(ma[a[i][0]]&&ma[a[i][1]]&&ma[a[i][0]]<ma[a[i][1]])
{
	swap(a[i][0],a[i][1]);
}

  • 遇到挫折难免影响情绪。在正式比赛的时候,你可以通过考前提醒设想此类情况,来让自己更快地接受现状,保持理智之心;但平时训练你难免产生情绪波澜,这个时候不妨强制自己趴下来休息一会儿,洗净过去的不愉快之后,再以崭新的面貌面对新的挑战
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed a[200005][2];
signed ma[200005];
bool v[200005],e[200005];
signed q[200005],tot;
bool dfs(int n1)
{
	v[n1]=true;
	q[++tot]=n1;
	for(int i=0;i<2;i++)
	{
		if(!ma[a[n1][i]]||v[ma[a[n1][i]]]==false&&e[ma[a[n1][i]]]==true&&dfs(ma[a[n1][i]]))
		{
			ma[a[n1][i]]=n1;
			return true;
		}
	}
	return e[n1]=false;
}
struct g1
{
	signed u,v,w;
}g[200005];
bool cmp(g1 a,g1 b)
{
	return a.w>b.w;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		cin>>n>>m;
		memset(ma,0,sizeof(ma[0])*(n+1));
		for(int i=1;i<=m;i++)
		{
			cin>>g[i].u>>g[i].v>>g[i].w;
		}
		sort(g+1,g+m+1,cmp);
		for(int i=1;i<=m;i++)
		{
			e[i]=true;
			a[i][0]=g[i].u;
			a[i][1]=g[i].v;
		}
		int ans=0;
		for(int i=1;i<=m;i++)
		{
			tot=0;
			if(ma[a[i][0]]&&ma[a[i][1]]&&ma[a[i][0]]<ma[a[i][1]])
			{
				swap(a[i][0],a[i][1]);
			}
			ans+=dfs(i)*g[i].w;
			for(int j=1;j<=tot;j++)
			{
				v[q[j]]=false;
			}
		}
		cout<<ans<<"\n";
	}
	return 0;
}

随机游走

  • 边双连通分量内的点(点数大于一),以及任意两个边双连通分量之间的点(树上统计),一定可以被纳入答案
  • 在剩下的树中,以边双为根,寻找\(u \rightarrow lca \rightarrow root \rightarrow lca \rightarrow v\)式的路径
#include <bits/stdc++.h>
#define int long long
using namespace std;
vector<int>a[200005];
vector<int>c[200005];
int fa[200005],dfn[200005],low[200005],h[200005],to[400005],nx[400005],tot,ans;
bool cut[400005],vis[200005];
int w[200005],d[200005];
void add(int u,int v)
{
	tot++;
	cut[tot]=false;
	to[tot]=v;
	nx[tot]=h[u];
	h[u]=tot;
}
int get(int x)
{
	if(fa[x]==x)
	{
		return x;
	}
	return fa[x]=get(fa[x]);
}
void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++tot;
	for(int i=h[u];i;i=nx[i])
	{
		int v=to[i];
		if(!dfn[v])
		{
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v])
			{
				cut[i]=cut[i^1]=true;
				c[to[i]].push_back(to[i^1]);
				c[to[i^1]].push_back(to[i]);
			}
		}
		else if(v!=fa)
		{
			low[u]=min(low[u],dfn[v]);
		}
	}
}
int num[200005];
void dfs1(int u)
{
	vis[u]=true;
	num[get(u)]++;
	for(int i=h[u];i;i=nx[i])
	{
		if(!cut[i])
		{
			int v=to[i];
			if(!vis[v])
			{
				fa[v]=get(u);
				w[fa[v]]+=w[v];
				w[v]=0;
				dfs1(v);
			}
		}
	}
}
void dfs2(int u)
{
	for(int v:a[u])
	{
		if(v!=fa[u])
		{
			fa[v]=u;
			d[v]=d[u]+w[u];
			dfs2(v);
		}
	}
}
void dfs3(int u)
{
	for(int v:a[u])
	{
		if(v!=fa[u])
		{
			d[v]=d[u]+w[u];
			dfs3(v);
		}
	}
}
int f[200005],l[200005];
void dp(int u)
{
	for(int v:a[u])
	{
		if(v!=fa[u])
		{
			dp(v);
			f[u]=max(f[u],l[u]+l[v]);
			l[u]=max(l[u],l[v]);
		}
	}
	f[u]+=w[u]+d[u];
	l[u]+=w[u];
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++)
		{
			a[i].clear();
			c[i].clear();
			dfn[i]=vis[i]=num[i]=f[i]=l[i]=0;
			h[i]=0;
			fa[i]=i;
		}
		for(int i=1;i<=n;i++)
		{
			cin>>w[i];
		}
		tot=1;
		for(int i=1;i<=m;i++)
		{
			int u,v;
			cin>>u>>v;
			add(u,v);
			add(v,u);
		}
		tot=0;
		tarjan(1,0);
		for(int i=1;i<=n;i++)
		{
			if(!vis[i])
			{
				dfs1(i);
			}
		}
		vector<int>dcc;
		for(int i=1;i<=n;i++)
		{
			if(i==fa[i]&&num[i]>1)
			{
				dcc.push_back(i);
			}
		}
		for(int i=2;i<=2*m+1;i+=2)
		{
			if(cut[i])
			{
				int u=get(to[i]),v=get(to[i^1]);
				a[u].push_back(v);
				a[v].push_back(u);
			}
		}
		ans=0;
		if(dcc.empty())
		{
			fa[1]=d[1]=0;
			dfs2(1);
			memset(d,0,sizeof(d[0])*(n+1));
			dp(1);
		}
		else
		{
			fa[dcc[0]]=d[dcc[0]]=0;
			dfs2(dcc[0]);
			memset(vis,false,sizeof(vis[0])*(n+1));
			vis[0]=true;
			for(int x:dcc)
			{
				while(!vis[x])
				{
					vis[x]=true;
					ans+=w[x];
					w[x]=0;
					x=fa[x];
				}
			}
			dfs3(dcc[0]);
			dp(dcc[0]);
		}
		cout<<ans+*max_element(f+1,f+n+1)<<endl;
	}
	return 0;
}
posted @ 2025-04-26 15:41  D06  阅读(51)  评论(0)    收藏  举报
//雪花飘落效果