2025 --【J+S 二十连测】-- 第十九套 总结+题解

总结

T1

考场上,想出了正解并打出了代码,没什么问题。

T2,T4

打了一个部分分,都拿到了。

题解

T1

很容易发现,第一种方式其实可以归到第2种方式上,所以我们直接按照第2种方式去每一位判断就行。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+5;
signed main()
{
	freopen("telecom.in","r",stdin);
	freopen("telecom.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		int n,cnt=1,ans=0;cin>>n;
		while(n) ans+=cnt*(n%10),cnt*=2,n/=10;
		cout<<ans<<endl;
	}
	return 0;
}

T2

如果只有要求他们相邻两项必须有最大公约数的话,我们可以直接去把每一个的末尾记录下来然后比出最大值就行了。

因为它有了第二个条件,所以我们只是要求出来一个最后一个值与最大值不一样的第二大的一个串。然后去看看到底是填这个优还是不填优即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=5e5+5;
int a[maxn],b[maxn];
vector<int> f(int x)
{
	vector<int> g;
	for(int j=2;j*j<=x;j++)
	{
		if(x%j==0)
		{
			g.push_back(j);
			while(x%j==0) x/=j;
		}
	}
	if(x>1) g.push_back(x);
	return g;
}
struct node
{
	int c,len;
}max1[maxn],max2[maxn];
signed main()
{
	freopen("lol.in","r",stdin);
	freopen("lol.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,ans=0;cin>>n;
	for(int i=1;i<=n;i++) cin>>b[i]>>a[i];
	for(int i=1;i<=n;i++)
	{
		vector<int> v=f(b[i]);
		int maxx=0;
		for(auto x:v)
		{
			if(a[i]==max1[x].c) maxx=max(maxx,max2[x].len);
			else maxx=max(maxx,max1[x].len);
		}
		maxx++,ans=max(ans,maxx);
		for(auto x:v)
		{
			if(max1[x].c==a[i]) max1[x].len=max(max1[x].len,maxx);
			else
				if(max1[x].len<maxx) max2[x]=max1[x],max1[x]={a[i],maxx};
				else if(max2[x].len<maxx) max2[x]={a[i],maxx};
		}
	}
	cout<<ans;
	return 0;
}

T3

会发现,这道题其实直接用一个最短路就可以过。因为它每一条边最多只走一次。但是你用最短路在这里会超时。

我们会发现这个图它有一定的性质。像这种有性质的图我们可以直接去DP。

那么DP的方程以及转移都跟原来最短路的数组是一样的。最后输出结果也是一样的但是这里可以去掉一只log。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
// #define int long long
#define endl '\n'
using namespace std;
const int maxn=505;
int dis[maxn][100005],sum=0;
struct node
{
	int x,y,z;
	bool operator <(const node &a) const
	{
		return z>a.z;
	}
};
vector<node> g[maxn];
signed main()
{
	freopen("medal.in","r",stdin);
	freopen("medal.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,m;cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v,y,z;cin>>u>>v>>y>>z;
		g[u].push_back({v,y,z}),sum+=y;
	}
	priority_queue<node> q;
	q.push({1,0,0});
	for(int i=0;i<=n;i++) for(int j=0;j<=sum;j++) dis[i][j]=inf;
	dis[1][0]=0;
	for(int i=0;i<=sum;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(dis[j][i]==inf) continue;
			int now=j,a=i;
			for(auto nxt:g[now])
			{
				int to=nxt.x,x=nxt.y,y=nxt.z;
				if(a+x<=sum&&dis[to][a+x]>dis[now][a]+y) dis[to][x+a]=dis[now][a]+y;
			}
		}
	}
	int ans1=1e5,ans2=1e5;
	// cout<<dis[n][63]<<endl;
	for(int i=1;i<=sum;i++)
	{
		// cout<<n<<' '<<i<<' '<<dis[n][i]<<endl;
		if(dis[n][i]!=inf&&ans1*ans2>i*dis[n][i]&&dis[n][i]*i>0) ans1=i,ans2=dis[n][i];
	}
	cout<<ans1<<' '<<ans2;
	return 0;
}

T4

会发现这里的N其实非常小但又刚好不能进形状压。

于是我们可以将所有的点分为两类。

  1. 当前这个点所在的协会只开了他这一家店。因为你会发现这道题它是一定是一个DAG。所以。可以无视掉每一个协会只能领一次红包的条件。
  2. 当前这个点所在的协会开了多家店。这个时候我们就可以装牙了,因为你会发现这种电不会超过18个。

\(dp_{i,j}\) 表示,到了当前点。已经经过了第2种条件中的哪些点将,他的状态压缩在 \(j\) 中。

那么转移就是 \(i\) 之前所有能够到 \(i\) 的点。如果以前已经走过当前这个点所对应的协会,那么就不加,否则就加。如果它是第一种点就直接加即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=40;
int dp[maxn][(1<<20)],a[maxn],b[maxn],c[maxn],d[maxn],f[maxn][maxn],cnt[maxn];
signed main()
{
	freopen("adventure.in","r",stdin);
	freopen("adventure.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,m,num=0;cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>b[i];
	for(int j=1;j<=n;j++) cin>>a[j],cnt[b[j]]++;
	for(int i=1;i<=n;i++) c[i]=a[b[i]];
	for(int i=1;i<=n;i++)
		if(cnt[i]>1) d[i]=num++;
		else d[i]=-1;
	// for(int i=1;i<=n;i++) cout<<cnt[i]<<' '<<c[i]<<' '<<d[i]<<endl;
	for(int i=1;i<=m;i++)
	{
		int u,v;cin>>u>>v;
		f[u][v]=1;
	}
	for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]|=(f[i][k]&f[k][j]);
	// for(int i=1;i<=n;i++)
		// for(int j=1;j<=n;j++) cout<<f[i][j]<<(j==n?endl:' ');
	if(d[b[1]]!=-1) dp[1][1<<d[b[1]]]=c[1];
	else dp[1][0]=c[1];
	for(int i=1;i<=n;i++)
	{
		for(int j=i-1;j>=1;j--)
		{
			if(!f[j][i]) continue;
			for(int k=0;k<(1<<num);k++)
			{
				if(d[b[i]]==-1) dp[i][k]=max(dp[i][k],dp[j][k]+c[i]);
				else if((k>>d[b[i]])&1) dp[i][k]=max(dp[i][k],dp[j][k-(1<<(d[b[i]]))]+c[i]);
				else dp[i][k]=max(dp[i][k],dp[j][k]);
			// cout<<j<<"->"<<i<<':'<<k<<"|"<<dp[i][k]<<endl;
			}
		}
		int ans=0;
		for(int j=0;j<(1<<num);j++) ans=max(ans,dp[i][j]);
		cout<<ans<<endl;
	}
	return 0;
}

T5

其实这里我们可以将当前目前所这个点以及它所对应的那个点。看做一个入度点一个出入点,那么这个时候就相当于给每一条边定一个想,使得它没有桥。

那么这个时候我就可以对于每一个联通块,我去跑他这个戍边,对于每个返祖边,我也按照戍边的这个方向给他去边好即可,然后我们将树边的某一个方向上的边的编号输出即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=5e6+5;
int n,m,a[maxn],b[maxn],cnt=1,vis[maxn],vis1[maxn],dis[maxn];
vector<pair<int,int> > g[maxn];
void dfs(int x,int fa)
{
	if(vis1[x]) return;
	vis1[x]=1;
	for(auto nxt:g[x])
	{
		int to=nxt.first,w=nxt.second;
		if((w^1)==fa||vis[w]) continue;
		vis[w^1]=vis[w]=1;
		dis[w]=1,dis[w^1]=0;
		dfs(to,w);
	}
}
signed main()
{
	freopen("bridge.in","r",stdin);
	freopen("bridge.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++) cin>>a[i];
	for(int i=1;i<=m;i++) cin>>b[i];
	for(int i=1;i<=m;i++) g[a[i]].push_back({b[i],++cnt}),g[b[i]].push_back({a[i],++cnt});
	for(int i=1;i<=n;i++) if(!vis1[i]) dfs(i,0);
	for(int i=1;i<=m;i++) cout<<dis[i*2];
	return 0;
}
posted @ 2025-12-14 14:29  Engle_Chen  阅读(3)  评论(0)    收藏  举报