2025 --【J+S 二十连测】-- 第十七套 总结

总结

T1

考场上想出来了解法。但是不太对。解的一个小策略,没想到。

T2

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

T3

考场上没想到正解也没有打出部分分。

T4 T5

考场上也没有打出正解。但是打出了部分分。

题解

T1

发现如果说发现如果说总当前这个数小于 \(s\) 则一定不行,如果大于 \(s+9\times 18\) 则一定可以。

对于中间的那些数,直接判断就行。

代码

#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("number.in","r",stdin);
	freopen("number.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,s,ans=0;cin>>n>>s;
	if(n>s+9*18) ans+=n-(s+9*18),n=s+9*18;
	for(int i=s;i<=n;i++)
	{
		int x=i,sum=0;
        while(x) sum+=(x%10),x/=10;
        if(i-sum>=s) ans++;
	}
	cout<<ans;
	return 0;
}

T2

不难发现,如果说当前这个在二进制下的1的个数和 \(n\) 模二同余。那么前面我可以用一大堆一来填最后我填当前这一个数每一位即可。

否则的情况下先特判掉。二进制下一的个数大于一的。那些答案我们可以向任意两位合并在一起算。即上面那个答案加一。

对于二进制下一的个数等于一的,我们可以将它和一组合在一起。这样子一定是最优的,而针对于一,我们让二和三去抹掉他就行。

代码

#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("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		int n,x;cin>>n>>x;
		if(__builtin_popcount(x)%2==n%2) cout<<max(0ll,n-__builtin_popcount(x))+x<<endl;
		else
		{
			if(x==1) cout<<n+3<<endl;
			else if(__builtin_popcount(x)>1) cout<<max(0ll,n-__builtin_popcount(x)+1)+x<<endl;
			else cout<<n+x<<endl;
		}
	}
	return 0;
}

T3

观察到题目有一个最小的最大,所以考虑使用二分。

考虑二分最后的答案,那么将所有删掉它的权值小于当前这个值的放到队列里面然后去做广搜。

最后看是否所有的点都进入过队列里即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+5;
int n,m,a[maxn],b[maxn],c[maxn],vis[maxn];
vector<int> g[maxn];
bool check(int x)
{
	int cnt=n;
	queue<int> q;
	for(int i=1;i<=n;i++)
		if(b[i]<=x) vis[i]=1,q.push(i),cnt--,c[i]=b[i];
		else vis[i]=0,c[i]=b[i];
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(auto to:g[now])
		{
			c[to]-=a[now];
			if(c[to]<=x&&!vis[to]) cnt--,q.push(to),vis[to]=1;
		}
	}
	return !cnt;
}
signed main()
{
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++)
	{
		int u,v;cin>>u>>v;
		b[u]+=a[v],b[v]+=a[u];
		g[u].push_back(v);
		g[v].push_back(u);
	}
	int l=1,r=inf,ans=inf;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<ans;
	return 0;
}

T4

我们会发现这里与草的顺序无关所以说我们可以将它。排序,然后再去做DP。

会发现,这里其实具体的操作次数非常少大约只有三十。

\(dp_{i,j}\) 表示考虑了前 \(i\) 个草。恰好操作 \(j\) 次的最少提前操作次数。

那么转移就是。\(dp{i,j} = \min(dp{i-1,j-1}, dp_{x,j} + 1)\)

最后去找到一个最小的合法值即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+5;
int a[maxn],dp[maxn][50];
signed main()
{
    freopen("grass.in","r",stdin);
	freopen("grass.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,k,ans=inf;cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	memset(dp,0x3f,sizeof(dp));
	sort(a+1,a+n+1);
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=40;j++)
		{
			dp[i][j]=dp[i-1][j]+1;
			int lst=upper_bound(a+1,a+1+i,a[i]/2)-a-1;
			if(j) dp[i][j]=min(dp[i][j],dp[lst][j-1]);
		}
	}
	for(int i=0;i<=40;i++)
		if(dp[n][i]<=k)
		{
			cout<<i<<' '<<dp[n][i];
			return 0;
		}
	return 0;
}

T5

我们可以通过部分来想到这一切的症结。

不难发现我们帮我们把一条边变为零之后。我要么走过他,要么不走过他。

所以我们直接按照这个可以就可以去计算了。

即算\(\sum_{1\le i,j\le n} c(i,j)-\max(0,dis_{i,j}-dis_{i,x}-dis_{y,j})\)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const int maxn=505;
int f[maxn][maxn],c[maxn][maxn],ans[maxn][maxn],from[maxn][maxn],to[maxn][maxn];
int mult[maxn][maxn],pre[maxn][maxn];
signed main()
{
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,q,sum=0;cin>>n>>q;
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>f[i][j];
	for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>c[i][j],sum+=f[i][j]*c[i][j];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++) from[i][j]=to[i][j]=j;
		sort(from[i]+1,from[i]+1+n,[&](int x,int y){return f[i][x]<f[i][y];});
		sort(to[i]+1,to[i]+1+n,[&](int x,int y){return f[x][i]<f[y][i];});
	}
	for(int s=1;s<=n;s++)
	{
		memset(mult,0,sizeof(mult));
		memset(pre,0,sizeof(pre));
		for(int t=1;t<=n;t++)
		{
			if(!c[s][t]) continue;
			int now=n;
			for(int i=1;i<=n;i++)
			{
				int v=to[t][i],x=f[s][t]-f[v][t];
				while(now>0&&x<=f[s][from[s][now]]) now--;
				if(!now) break;
				pre[v][now]+=c[s][t]*(f[s][t]-f[v][t]);
				mult[v][now]+=c[s][t]; 
			}
		}
		for(int v=1;v<=n;v++)
			for(int i=n;i>=1;i--)
			{
				pre[v][i]+=pre[v][i+1];
				mult[v][i]+=mult[v][i+1];
				ans[from[s][i]][v]-=mult[v][i]*f[s][from[s][i]];
				ans[from[s][i]][v]+=pre[v][i];
			}
	}
	while(q--)
	{
		int x,y;cin>>x>>y;
		cout<<sum-ans[x][y]-ans[y][x]<<endl;
	}
	return 0;
}
posted @ 2025-12-06 11:22  Engle_Chen  阅读(3)  评论(0)    收藏  举报