多校A层冲刺NOIP2024模拟赛09

GG

多校A层冲刺NOIP2024模拟赛09

T1 排列最小生成树 (pmst)

需要思考一会。

你得发现一个性质:所有要的边的权值都得小于 n ,因为每个点都可以找到至少一条边权小于 n 的边,所以最后生成树里的边的边权一定小于 n 。

那么 $ \vert p_i - p_j \vert \times \vert i - j \vert $ 中较小的那个一定小于等于 $ \sqrt{n} $ ,所以直接 $ O (n \sqrt{n} ) $ 枚举建边即可,然后你就会因为把优先队列炸了而获得不如暴力的 $ 30 $ 分高分。

直接拿数组记就行了,然后 $ O(n) $ 枚举也可,直接 sort 也能过。

挂 70。。。。。。。。。。。。。。。。。。。。。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mk make_pair
#define ps push_back
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
inline ll read(){
	char c=getchar();ll x=0,f=1;
	while(!isdigit(c))f=c=='-'?0:-1,c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return f?x:-x;
}
int n,a[N],len,fa[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
struct jj{
	int i,j,v;
	inline bool operator <(const jj&x)const{return v>x.v;}
}q[N<<4];
int pos[N];
unordered_map<int,bool> ma;
signed main(){

	// #ifndef ONLINE_JUDGE
	freopen("pmst.in","r",stdin);
	freopen("pmst.out","w",stdout);
	// #endif

	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	n=read();
	for(int i=1;i<=n;++i)
		a[i]=read(),fa[i]=i;
	int len=sqrt(n),cnt=0;
	
	for(int i=1;i<=n;++i){
		ma.clear();
		for(int j=i-1;j&&i-j<=len;--j){
			if((i-j)*abs(a[i]-a[j])<=n&&!ma[j])q[++cnt]={i,j,(i-j)*abs(a[i]-a[j])},ma[j]=1;
		}
		for(int j=a[i];j<=n&&j<=a[i]+len;++j)
			if(pos[j]&&(i-pos[j])*(j-a[i])<=n&&!ma[pos[j]])q[++cnt]={i,pos[j],(i-pos[j])*(j-a[i])},ma[pos[j]]=1;
		for(int j=a[i];j&&j>=a[i]-len;--j)
			if(pos[j]&&(i-pos[j])*(a[i]-j)<=n&&!ma[pos[j]])q[++cnt]={i,pos[j],(i-pos[j])*(a[i]-j)},ma[pos[j]]=1;
		pos[a[i]]=i;
	}
	int num=0;
	ll ans=0;
	sort(q+1,q+1+cnt,[](const jj&x,const jj&y){return x.v<y.v;});
	for(int i=1;i<=cnt&&num<n-1;++i){
		int fx=find(q[i].i),fy=find(q[i].j);
		if(fx!=fy){
			++num;fa[fx]=fy;ans+=q[i].v;
		}
	}
	cout<<ans;


	return 0;
}

T2 卡牌游戏 (cardgame)

签到题。

首先为了方便讨论,我们假设 $ n \le m $ ,那么对于每一个 $ a_i $ ,他会对应一些 $ b_j (j=(i+kn) \mod m | k < n)$ 。这些 $ b_j $ 是一个循环,那么我们只要记下所有循环节,并对于所有的 $ b_j $ ,把他属于的的循环节的编号记下来,对于每个 $ a_i $ 他肯定对应着 $ b_i $ (因为 n < m),所以就能找到与 $ a_i $ 相对应的循环节,然后直接 lower_bound 即可。

别忘了一开始 swap 保证 $ n \le m $ ,最后答案也要 swap 回去。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mk make_pair
#define ps push_back
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
inline ll read(){
	char c=getchar();ll x=0,f=1;
	while(!isdigit(c))f=c=='-'?0:-1,c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return f?x:-x;
}
int n,m,a[N],b[N],cnt;
int id[N];
#define ne(x) (x+n>m?x+n-m:x+n)
vector<int> v[N/10];
signed main(){

	// #ifndef ONLINE_JUDGE
	freopen("cardgame.in","r",stdin);
	freopen("cardgame.out","w",stdout);
	// #endif

	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

	n=read(),m=read();
	for(int i=1;i<=n;++i)
		a[i]=read();
	for(int i=1;i<=m;++i)
		b[i]=read();
	bool f=0;
	if(n>m)swap(n,m),swap(a,b),f=1;
	int gc=__gcd(n,m);
	for(int i=1;i<=m;++i){
		if(!id[i]){
			id[i]=++cnt;v[cnt].ps(b[i]);
			for(int j=ne(i);j!=i;j=ne(j))
				id[j]=cnt,v[cnt].ps(b[j]);
			sort(v[cnt].begin(), v[cnt].end());
		}
	}
	ll ans1=0,ans2=0,ans3=0;
	for(int i=1;i<=n;++i){
		int pos=lower_bound(v[id[i]].begin(),v[id[i]].end(),a[i])-v[id[i]].begin(),pos2=upper_bound(v[id[i]].begin(), v[id[i]].end(),a[i])-v[id[i]].begin();
		ans1+=pos,ans2+=pos2-pos,ans3+=v[id[i]].size()-pos2;
	}
	ans1*=gc;ans2*=gc,ans3*=gc;
	if(f)swap(ans1,ans3);
	cout<<ans1<<'\n'<<ans3<<'\n'<<ans2;

	return 0;
}

T3 比特跳跃 (jump)

竟然是一道分讨题。

对于 & 操作

数越 & 越小,然后你就可以发现一个性质:只要最大点不是 $ 1111111_{(2)} $ 这类数,或者说他只要在小于等于 n 的范围内有一个 0 ,那么他一定可以通过某个数耗费 0 的代价转移过来。如果他全是 1 的话,那么就只能选一个跟他有关的最小边或者直接通过 1 转移过来。

所以直接特判 n 即可。

对于 ^ 操作

^ 其实就是看两个数二进制之间有多少位不一样,然后你又可以发现:先不一样一个 1 ,再不一样一个 2 ,和直接不一样一个 3 是等价的,所以我们只建有一位不一样的边即可,边数是 $ O (m+ nlog(n)) $ 的。

然后跑 dij 就行了。

对于 | 操作

数越 | 越大,所以通过|操作最多只转移 1 次,而且因为起点是 1 ,那么从 1 去转移 $ i $ ,耗费最大是 $ K \times (i+1) $ ,而且无论从哪些点转移到 $ i $ ,最小耗费也得是 $ K \times i $ ,那么哪些可以只消耗 $ K \times i $ 转过来呢,只有 $ i $ 的子集可以做到,那么我们不能枚举所有子集吧,那就只枚举少一个 1 的子集,此时用 $ tag_i $ 表示 $ i 和 i 的子集 $ 的 $ dis $ 的最小值,这样就可以 $ O (nlog(n)) $ 转移了。

所以只需要加上 1 到所有点 | 的边,跑一边 $ dij $ ,然后枚举 | 转移,然后再跑一边 $ dij $ 即可。

为什么只需要跑两遍 $ dij $ ?因为之前说过通过 | 只会转移一次。所以只 | 转移一次就行。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mk make_pair
#define ps push_back
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
inline ll read(){
	char c=getchar();ll x=0,f=1;
	while(!isdigit(c))f=c=='-'?0:-1,c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return f?x:-x;
}
#define int ll
int n,m,cnt,hd[N],S,K;
struct jj{
	int to,next;ll w;
}bi[N<<3];
inline void add(int x,int y,ll z){bi[++cnt]={y,hd[x],z},hd[x]=cnt,bi[++cnt]={x,hd[y],z},hd[y]=cnt;}
ll dis[N],adis[N];
bool vis[N];
inline void dij(){
	fill(vis+1,vis+1+n,0);
	dis[1]=0;
	priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > >q;
	for(int i=1;i<=n;++i)
		q.push({dis[i],i});
	// q.push({dis[1],1});
	while(!q.empty()){
		int k=q.top().se;q.pop();
		if(vis[k])continue;
		// cout<<k<<' '<<dis[k]<<"DIJ\n";
		vis[k]=1;
		for(int i=hd[k];i;i=bi[i].next){
			int j=bi[i].to;
			if(dis[j]>dis[k]+bi[i].w)
				dis[j]=dis[k]+bi[i].w,q.push({dis[j],j});
		}
	}
}
signed main(){

	// #ifndef ONLINE_JUDGE
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	// #endif

	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	n=read(),m=read();S=read(),K=read();
	// cout<<S<<','<<K<<',';
	for(int i=1,x,y,z;i<=m;++i){
		x=read(),y=read(),z=read();
		add(x,y,z);
		// cout<<x<<','<<y<<','<<z<<',';
	}
	if(S==1){
		for(int i=2;i<n;++i)
			cout<<0<<' ';
		if((int)log2(n+1)!=(int)log2(n)){
			int ans=inf;
			for(int i=1;i<=cnt;++i){
				if(bi[i].to==n)ans=min(ans,bi[i].w);
			}
			cout<<min(ans,K);
		}
		else{
			cout<<0;
		}
		return 0;
	}
	if(S==2){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;j<<=1)
				if((i^j)<=n)add(i,i^j,j*K);
		}
		for(int i=2;i<=n;++i)
			add(1,i,K*(i^1));
		fill(dis+1,dis+1+n,linf);
		dij();
		for(int i=2;i<=n;++i)
			cout<<dis[i]<<' ';
		return 0;
	}
	else{
		// cout<<n<<','<<m<<',';
		// return 0;
		for(int i=2;i<=n;++i)
			add(1,i,K*(i|1));
		fill(dis+1,dis+1+n,linf);
		dij();
		adis[1]=0;
		for(int i=2;i<=n;++i){
			adis[i]=linf;
			for(int j=1;j<i;j<<=1){
				if((i&j)){
					adis[i]=min(adis[i],adis[i^j]);
					// cout<<i<<' '<<j<<' '<<dis[j]<<' '<<dis[i]<<endl;
				}
			}
			adis[i]=min(adis[i],dis[i]);
			dis[i]=min(dis[i],adis[i]+i*K);
			// cout<<i<<' '<<dis[i]<<endl;
		}
		dij();
		for(int i=2;i<=n;++i)
			cout<<dis[i]<<' ';
		return 0;
	}
	// for(int i=1;i<=n;++i){
	// 	for(int j=i+1;j<=n;++j){
	// 		add(i,j,K*(S==1?(i&j):S==2?(i^j):(i|j)));
	// 		// cout<<i<<' '<<j<<' '<<(i|j)<<endl;
	// 	}
	// }
	// fill(dis+1,dis+1+n,linf);
	// dij();
	// for(int i=2;i<=n;++i)
	// 	cout<<dis[i]<<' ';
	// return 0;
	


	return 0;
}

T4 区间 (interval)

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

线段树记得push_down!!! 线段树记得push_down!!! 线段树记得push_down!!!

好的,这就是我爆 0 的原因。

这题其实就是个求区间历史和的板子。

我们可以用离线下来按照 $ r $ 排序,然后单调栈维护可以贡献的区间有哪些,但是发现这些区间并不是连续的,也就有可能被卡成 $ O(n^2) $ 的,但是因为这是一段后缀抛去了一些被单调栈弹出去的数,那么我们给这些被弹出去的数打上tag,然后直接区间加就行了。

挂 100。

如果你想看更详细的题解的话可以去 G-数据结构-G

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define int ll
typedef pair<int,int> pii;
#define fi first
#define se second
#define mk make_pair
#define ps push_back
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
inline ll read(){
	char c=getchar();ll x=0,f=1;
	while(!isdigit(c))f=c=='-'?0:-1,c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return f?x:-x;
}
int n,Q,a[N],b[N];
struct jj{
	int l,r,id;
}q[N];
ll sum[N<<2],tag[N<<2],c[N<<2],ta[N<<2];
inline void down(int k,int len1,int len2){
	if(ta[k])sum[k<<1]+=ta[k]*(len1-tag[k<<1]),sum[k<<1|1]+=ta[k]*(len2-tag[k<<1|1]),ta[k<<1]+=ta[k],ta[k<<1|1]+=ta[k],ta[k]=0;
}
inline void add(int k,int l,int r,int pos){
	if(l==r)return (void)(++tag[k]);
	int mid=l+r>>1;
	down(k,mid-l+1,r-mid);
	pos<=mid?add(k<<1,l,mid,pos):add(k<<1|1,mid+1,r,pos);
	tag[k]=tag[k<<1]+tag[k<<1|1];
}
inline void add(int k,int l,int r,int L,int R){
	if(L<=l&&r<=R)return (void)(sum[k]+=r-l+1-tag[k],ta[k]++);
	int mid=l+r>>1;
	down(k,mid-l+1,r-mid);
	if(L<=mid)add(k<<1,l,mid,L,R);
	if(R>mid)add(k<<1|1,mid+1,r,L,R);
	sum[k]=sum[k<<1]+sum[k<<1|1];
}
inline int ask(int k,int l,int r,int L,int R){
	if(L<=l&&r<=R)return sum[k];
	int mid=l+r>>1,ans=0;
	down(k,mid-l+1,r-mid);
	if(L<=mid)ans=ask(k<<1,l,mid,L,R);
	if(R>mid)ans+=ask(k<<1|1,mid+1,r,L,R);
	return ans;
}
int top;
pii st[N];
ll ans[N];
signed main(){

	// #ifndef ONLINE_JUDGE
	freopen("interval.in","r",stdin);
	freopen("interval.out","w",stdout);
	// #endif

	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	n=read();
	for(int i=1;i<=n;++i)
		a[i]=read();
	for(int i=2;i<=n;++i)
		b[i]=read();
	Q=read();
	for(int i=1;i<=Q;++i){
		q[i].l=read(),q[i].r=read();q[i].id=i;
	}
	sort(q+1,q+1+Q,[](const jj&x,const jj&y){return x.r<y.r;});
	for(int i=1,j=1;i<=n;++i){
		int l=1,r=top,pos=0;
		while(l<=r){
			int mid=l+r>>1;
			if(st[mid].se<b[i])pos=mid,r=mid-1;
			else l=mid+1;
		}
		// cout<<i<<' '<<pos<<' '<<st[pos].fi<<endl;
		if(pos&&st[pos].fi<i)add(1,1,n,st[pos].fi,i-1);
		while(j<=Q&&q[j].r==i){
			ans[q[j].id]=ask(1,1,n,q[j].l,i),++j;
		}
		while(top&&st[top].se<=a[i])add(1,1,n,st[top].fi),--top;
		st[++top]={i,a[i]};
	}

	for(int i=1;i<=Q;++i)
		cout<<ans[i]<<'\n';
	return 0;
}
posted @ 2024-10-19 21:45  lzrG23  阅读(31)  评论(0编辑  收藏  举报