国庆模拟赛二总结

垃圾梦熊服务器真烂

T1

link

非常简单的题,一眼扫描线,但是™的扫描线竟然会TLE,只有80pts,我是真的服了(虽然正解简单到爆)。

正解好像是珂以从末开始直接一遍 1e7 循环扫过去每次加最大值就可以了。

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long  不开longlong见祖宗
#define ls now<<1
#define rs now<<1|1
const ll N=20000005,M=1919810,inf=1145141919;
ll n,maxn[N];
struct xx{
	ll x,y;
}a[N];
int main(){
	//freopen("unija.in","r",stdin);
	//freopen("unija.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0); 
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i].x>>a[i].y,a[i].x/=2,a[i].y/=2,maxn[a[i].x]=max(maxn[a[i].x],a[i].y);
	//sort(a+1,a+n+1,cmp); 
	ll ans=0,y=0;
	for(int i=1e7;i>=1;--i)
		y=max(y,maxn[i]),ans+=y;
	cout<<ans*4;
	return 0;
}

T2

link

这个题赛时推了个前真后假的结论,喜提15pts,本来也想到冰茶姬了结果一开始的方向就错了/ng

其实这题就是一个简单的权值并查集!甚至都和树的形态没有关系!只需要将题目的条件转化成 disudisv=x 就行了,其中 disi 表示i到根的边权异或前缀和。

而且并查集维护的是点而不是我想的边,最后看并查集里面有几个根结果就是 2n1。还要注意特判无解的情况。

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
const ll N=1145140,M=1919810,mod=1e9+7;
ll n,Q;
ll qpow(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans%mod;
}
ll f[N],dis[N];
ll find(ll x){
	if(x==f[x]) return x;
	else{
		ll pos=f[x];
		f[x]=find(f[x]);
		dis[x]=dis[x]^dis[pos];
		return f[x];
	}
}
void merge(ll x,ll y,ll z){
	ll a=find(x),b=find(y);
	f[b]=a;
	dis[b]=dis[x]^dis[y]^z;
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	ll ans=0,cnt=0;
	cin>>n>>Q;
	for(int i=1;i<n;++i){
		ll a,b;
		cin>>a>>b;
		//add(a,b),add(b,a); 甚至不需要建树 
	}
	for(int i=1;i<=n;++i) f[i]=i; //千万别忘了
	for(int i=1;i<=Q;++i){
		ll u,v,w;
		cin>>u>>v>>w;
		ll x=find(u),y=find(v);
		if(x==y&&dis[u]!=(dis[v]^w)){ //如果重复出现同一对点且w不同,则无解 
			cout<<0;
			return 0;
		}
		if(x!=y) merge(u,v,w);
	}
	for(int i=1;i<=n;++i)
		if(find(i)==i) ++cnt;
	cout<<qpow(2,cnt-1);
	return 0;
}

T3

link

dp,推不来,只能说还是能力的问题了,还是得多做题而且要精做题,每写一道都得整一个题解写起,确保自己完全懂了回了并且下一次(虽然大概率没有)就能用上一些思维trick。

回到题目,先讨论70pts的dp。首先设 dpi,j,k 为处理完前i个任务,机器一还剩j时间的工作,机器二还剩k时间的工作时的最小工作时间(这个状态就已经是我设不出来的东西了/kel)。

转移:第i个任务是由 dpi1,j,k 转移而来的,那么在第一台机器上的转移便是 dpi,t1,t2=min(dpi,t1,t2,dpi1,j,k+cost)

其中若机器一需要的时间更多,需要考虑特殊情况:if(j>=k) cost=a[i],t1=a[i],t2=0;

或者机器二需要的时间多:if(j+a[i]<=k) cost=0,t1=j+a[i],t2=k;

一般的转移便是cost=j+a[i]-k,t1=j+a[i]-k,t2=0;

特殊情况下:如果第⼀台机器的完成时间已经⽐第⼆台要晚了,这时仍然把任务交个第⼀台机器的时候,两台机器的时间差就变为了 ai,⽽不是在原有的 j 的基础上加上 ai,原因是需要等第⼀台的上⼀个任务完成,才能在第⼆台机器上开始任务,这样时间才不会错。(题解说的)

机器二的转移和这里类似。总的复杂度就是 O(nm2)m 表示完成某个任务的最长时间。

剩下30pts要求只能 O(nm),考虑将后两维通过他们之间的关系压缩成一维。

那么可以这样设:dpi,j 为完成前i个任务,机器一剩余工作时间比机器二剩余工作时间多了j时间的机器一最小工作时间。机器二的工作时间珂以通过 dpi,j+j 来计算。

还要注意j可能为负数,所以转移的时候加上一个花费时间最大值M就可以了。

大致就是这样,具体思维过程还需要进一步体会啊。

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
const ll N=3003,M=1919810,mod=1e9+7;
ll n;
struct xx{
	ll ta,tb;
}a[N];
ll dp[N][2*N+5];
int main(){
	freopen("task.in","r",stdin);
	freopen("task.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i].ta>>a[i].tb;
	memset(dp,63,sizeof(dp)); dp[0][N]=0;
	for(int i=1;i<=n;++i)
		for(int j=0;j<=2*N;++j){ //避免差值为负加了个N 
			if(j>=N){
				dp[i][N+a[i].ta]=min(dp[i][N+a[i].ta],dp[i-1][j]+a[i].ta);
				dp[i][j-a[i].tb]=min(dp[i][j-a[i].tb],dp[i-1][j]);
			}
			else{
				dp[i][N-a[i].tb]=min(dp[i][N-a[i].tb],dp[i-1][j]+(N-j));
				dp[i][j+a[i].ta]=min(dp[i][j+a[i].ta],dp[i-1][j]+a[i].ta);
			}
		}
	ll ans=mod;
	for(int i=0;i<=2*N;++i) ans=min(ans,dp[n][i]+max(0ll,N-i));
	cout<<ans;
	return 0;
}

T4

link

说实话不想补了,一开始连样例都模拟不出来。正解是哈希,但我竟然不会哈希,所以先不管它了。

摆了,去写图论了。

posted @   和蜀玩  阅读(13)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示