[题解]test0406模拟赛

A - exercise

照题意模拟即可,注意每天先增后减。细节见代码。

时间复杂度\(O(m\log m)\)

点击查看代码
#include<bits/stdc++.h>
#define M 514
#define int long long
using namespace std;
struct task{int l,r,w;}a[M];
int n,m;
string s;
signed main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>m;
	for(int i=1;i<=m;i++) cin>>s>>a[i].l>>a[i].r>>a[i].w;
	sort(a+1,a+1+m,[](task a,task b){return a.l<b.l;});
	for(int i=1;i<=m;i++){
		n+=a[i].l-a[i-1].r-1;
		int t=(1-a[i].w)*(a[i].r-a[i].l+1);
		if(n+t<=0) cout<<"Runtime Error\n"<<a[i].l+(n-1)/(a[i].w-1),exit(0);
		//                                  ↑相当于a[i].l+⌈n/(a[i].w-1)⌉-1
		n+=t;
	}
	n+=1440-a[m].r;
	cout<<"Accepted\n"<<n<<"\n";
	return 0;
}

B - 魔兽争霸

区间修改、查询名次等操作通常可以用平衡树在\(O((n+m)\log n)\)的时间复杂度中完成。不过放在这道题,我们也可以使用线段树达到同样的时间复杂度。

先对中途所有可能出现的血量(最多有\(n+m\)个)进行离散化,然后用线段树作为桶数组(现在才知道这叫做权值线段树)。应对\(2\)种操作:

  • 血量修改:血量从\(x\)变成\(y\),在线段树上仅需将\(x\)\(-1\)\(y\)\(+1\)
  • 查询第\(k\)大:采用线段树上二分,对于当前节点对应的区间\([l,r]\),令该区间的第\(k\)大是\(f(l,r,k)\),则:
    • 如果\([mid+1,r]\)中数的个数\(cnt\ge k\),说明第\(k\)名在\([mid+1,r]\)中,那么\(f(l,r,k)=f(mid+1,r,k)\)
    • 如果\([mid+1,r]\)中数的个数\(cnt<k\),说明第\(k\)名在\([l,mid]\)中,那么\(f(l,r,k)=f(l,mid,k-cnt)\)

每个操作时间复杂度都是\(O(\log n)\),算上离散化的\(O(n\log n)\),总时间复杂度为\(O((n+m)\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define lc (x<<1)
#define rc (x<<1|1)
#define N 30010
#define M 50010
using namespace std;
struct Que{char op;int p,v;}q[M];
int n,m,a[N],ta[N],tmp[(N+M)],idx,ans;
int sum[(N+M)<<2],tag[(N+M)<<2];
unordered_map<int,int> to;
void update(int x){sum[x]=sum[lc]+sum[rc];}
void pushdown(int l,int r,int x){
	if(tag[x]!=0){
		int mid=(l+r)>>1;
		tag[lc]+=tag[x];
		tag[rc]+=tag[x];
		sum[lc]+=tag[x]*(mid-l+1);
		sum[rc]+=tag[x]*(r-mid);
		tag[x]=0;
	}
}
void build(int l,int r,int x){
	if(l==r){
		sum[x]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,lc);
	build(mid+1,r,rc);
	update(x);
}
void chr(int a,int b,int v,int l,int r,int x){
	if(a<=l&&r<=b){
		tag[x]+=v;
		sum[x]+=v*(r-l+1);
		return;
	}
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(a<=mid) chr(a,b,v,l,mid,lc);
	if(b>mid) chr(a,b,v,mid+1,r,rc);
	update(x);
}
int query(int k,int l,int r,int x){
	if(l==r) return tmp[l]<=0?-1:tmp[l];
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(sum[rc]>=k) return query(k,mid+1,r,rc);
	else return query(k-sum[rc],l,mid,lc);
}
signed main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],tmp[++idx]=ta[i]=a[i];
	cin>>m;
	for(int i=1;i<=m;i++){
		cin>>q[i].op;
		if(q[i].op=='A'){
			cin>>q[i].p>>q[i].v,q[i].op='+',q[i].v=-q[i].v;
			tmp[++idx]=(ta[q[i].p]+=q[i].v);
		}else if(q[i].op=='C'){
			cin>>q[i].p>>q[i].v,q[i].op='+';
			tmp[++idx]=(ta[q[i].p]+=q[i].v);
		}else cin>>q[i].v;
	}
	sort(tmp+1,tmp+1+idx);
	int len=unique(tmp+1,tmp+1+idx)-tmp-1;
	for(int i=1;i<=len;i++) to[tmp[i]]=i;
	build(1,len,1);
	for(int i=1;i<=n;i++) chr(to[a[i]],to[a[i]],1,1,len,1);
	for(int i=1;i<=m;i++){
		if(q[i].op=='+'){
			chr(to[a[q[i].p]],to[a[q[i].p]],-1,1,len,1),
			a[q[i].p]+=q[i].v,
			chr(to[a[q[i].p]],to[a[q[i].p]],1,1,len,1);
		}else cout<<query(q[i].v,1,len,1)<<"\n";
	}
	for(int i=1;i<=n;i++) ans+=(a[i]>0);
	cout<<ans<<"\n";
	return 0;
}

线段树代码搬的之前的模板,所以单点修改都是直接调用的区间修改。实际上用不着区间修改。

C - 盟军敢死队

考虑建图求解,将敌人看做节点,如果\(u\)看向\(v\),就连一条\(u\)\(v\)的边。

这样建出的图\(G\),按其拓扑序击杀,一定是合法的。

相应地,如果\(G\)存在环,则无法全部击杀,输出Impossible


排除了不合法的情况,\(G\)就是一个DAG(有向无环图)。根据上面的分析,其不同拓扑序的数量即为答案。

考虑到节点数\(T\le 15\),所以考虑直接状压dp。用一个01串\(x\)来表示我们所选定的节点集合\(A\),节点\(u\)\(A\)中,当且仅当\(x\)的第\(u\)位是\(1\)

\(f[x]\)表示所选的节点集合为\(A\)时的拓扑序计数。

考虑在\(A\)的基础上添加一个节点\(u\),那么必须满足:

  • \(u\)不在\(A\)中。
  • \(u\)的子节点都在\(A\)中。

令新集合对应的整数为\(x'\),那么有转移\(f[x']\leftarrow (f[x']+f[x])\)

完成递推后,\(f[2^T-1]\)即为答案。

时间复杂度\(O(2^T T+nm\max(n,m))\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 61
#define NN 15
using namespace std;
struct edge{int nxt,to;}e[NN*N];
int n,m,dx[256],dy[256],idx,head[NN+5],deg[NN+5],cnt,num[N][N],enemy;
int f[1<<NN],son[1<<NN];
string s[N];
void add(int u,int v){
	e[++idx]={head[u],v},head[u]=idx;
	deg[v]++,son[u]|=(1<<v);
}
int frac(int x){int ans=1;for(int i=2;i<=x;i++) ans*=i;return ans;}
queue<int> q;
signed main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	memset(num,-1,sizeof num);
	dx['^']=-1,dy['^']=0;
	dx['v']=1,dy['v']=0;
	dx['<']=0,dy['<']=-1;
	dx['>']=0,dy['>']=1;
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>s[i];
	for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(dx[s[i][j]]|dy[s[i][j]]) num[i][j]=enemy++;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			if(num[i][j]==-1) continue;
			for(int x=i+dx[s[i][j]],y=j+dy[s[i][j]];;x+=dx[s[i][j]],y+=dy[s[i][j]]){
				if(x<0||x>=n||y<0||y>=m||s[x][y]=='#') break;
				if(~num[x][y]) add(num[i][j],num[x][y]);
			}
		}
	}
	int cnt=0;
	for(int i=0;i<enemy;i++) if(!deg[i]) q.emplace(i);
	while(!q.empty()){
		int u=q.front();
		q.pop(),cnt++;
		for(int i=head[u];i;i=e[i].nxt){
			deg[e[i].to]--;
			if(!deg[e[i].to]) q.push(e[i].to);
		}
	}
	if(cnt!=enemy) cout<<"Impossible\n",exit(0);
	f[0]=1;
	for(int i=0;i<(1<<enemy);i++){
		for(int j=0;j<enemy;j++){
			if(i&(1<<j)) continue;
			if((i&son[j])!=son[j]) continue;
			f[i|(1<<j)]+=f[i];
		}
	}
	cout<<f[(1<<enemy)-1]<<"\n";
	return 0;
}

D - orbs

可以想到一个很暴力的做法。

将边按权值排序。

为了使边权极差最小,我们所选的边要尽可能是一个连续的区间。

那么我们不妨枚举这个区间的左端点。对于每个左端点,从它开始逐个加边。这样每个左端点都跑出一个生成树,这些生成树的极差求最小值即为答案。

时间复杂度每个测试数据\(O(m(n+m))\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 105
#define M 5005
using namespace std;
struct edges{int u,v,w;}es[M];
int t,n,m,fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
signed main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	while(cin>>n>>m){
		if(!n&&!m) break;
		for(int i=1;i<=m;i++) cin>>es[i].u>>es[i].v>>es[i].w;
		sort(es+1,es+1+m,[](edges a,edges b){return a.w<b.w;});
		int ans=LLONG_MAX;
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++) fa[j]=j;
			int cnt=0,ww=-1;
			for(int j=i,u,v;j<=m;j++){
				u=find(es[j].u),v=find(es[j].v);
				if(u!=v) fa[u]=v,ww=es[j].w,cnt++;
			}
			if(cnt!=n-1) continue;
			ans=min(ans,ww-es[i].w);
		}
		cout<<(ans==LLONG_MAX?-1:ans)<<"\n";
	}
	return 0;
}
posted @ 2025-04-06 22:50  Sinktank  阅读(113)  评论(0)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.