2025/7/12模拟赛总结

2025/7/12\(\mathbf{} \begin{Bmatrix} \frac{{\Large TEST} }{{\color{Yellow}\Large Record} }\mathbf{} {No.5} \end{Bmatrix}\times{}\) NeeDna

题目链接

难度主观估计:\({\color{Orange} t1} <{\color{Blue} t2} <{\color{Blue} t4} <{\color{Purple} t3}\)

t1

题意:有一个序列 \(a\),有 \(K\) 次操作,每次对 \(a\) 桶排序,再把桶的值赋回 \(a\),求 \(a\)序列的最终形态。

想法:

比赛的时候感觉次数 \(K\) 多的时候一定会是 1 0 0 0 0 ···。所以用随机数据跑了一下。

发现 \(K\)\(10\) 次之后就都是 1 0 0 0 0 ···。所以模拟一下就过了。(30min)

证明:

每进行一次操作,不同的数的数量会变成 \(\sqrt n\),因此经过 $\log \log n $ 次操作就会不变。

计算一下是 \(5\) 次左右,开大一点就可以了

code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,M=1e7+10;
int n,k,a[N],b[N];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	if(k<=20||n*k<=M){
	    while(k--){
			for(int i=1;i<=n;i++){if(a[i])b[a[i]]++;}
			for(int i=1;i<=n;i++){a[i]=b[i];b[i]=0;}
		}
		for(int i=1;i<=n;i++) cout<<a[i]<<" ";
	}
	else{
		cout<<1<<" ";
		for(int i=1;i<n;i++) cout<<0<<" ";
	}
	return 0;
}

t2(link在上面)

感觉被资本做局了,挂了30pts,赛后又交了一份就过了 :<
给定 \(n\) 个非空序列 \(\{a_i\}^{l_i}_{j=1}\),定义 \(f(a)\) 表示按顺序将序列 \(a\) 的元素插入严格递增的单调栈,最后得到的栈的大小。

定义 \(a+b\) 是将序列 \(a,b\) 拼接而成的序列。求 \(\sum_{i=1}^n\sum_{j=1}^{n} f(a_i+a_j)\)

\(\{a_i\}_{i=1}^l\) 的单调栈大小为 \(\sum_{i=1}^l \prod_{j=i+1}^{l} [a_j > a_i]\)

按照题目意思,我们先来看 \(\sum_{i=1}^l \prod_{j=i+1}^{l} [a_j > a_i]\) 这个条件,其实就是从右向左更新最小值的过程。容易做到 \(O(n)\)

接下来看怎么把两个串合并,可以看出来,如果现在求 \(f(a,b)\)\(b\) 部分的答案是显然的,\(a\) 部分的答案满足是 \(a\) 的答案而且要比 \(b\) 中的最小值小。

那么把满足 \(\sum_{i=1}^l \prod_{j=i+1}^{l} [a_j > a_i]\) 的值存到一个树状数组里,之后就可以算ans了。

答案一部分为 \(自己的答案数 × n\),另一部分就从树状数组里取比自己序列的最小值小的个数和。两部分加起来就可以了。

code:

#include<bits/stdc++.h>
#define int long long
#define lowbit(x) (x&(-x))
using namespace std;
const int N=1e6+10;
int n,ans,t[N],cnt,b[N],a[N];
vector<int> g[N],qz[N];
struct node{int mi,sum,id;}h[N];
bool cmp(node a,node b){return a.mi<b.mi;}
void upd(int x,int i){while(x<N){t[x]+=i;x+=lowbit(x);}}
int sc(int x){int ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans;}
void getup(int x){for(int v:qz[x]){upd(v,1);}}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1,u,k;i<=n;i++){cin>>k;
    	for(int j=1;j<=k;j++){cin>>u;a[k-j+1]=u;b[++cnt]=u;}
		for(int j=1;j<=k;j++){g[i].push_back(a[j]);}
	}sort(b+1,b+cnt+1);
	for(int i=1;i<=n;i++){
		for(int &v:g[i]){int x=lower_bound(b+1,b+cnt+1,v)-b;v=x;}
	}for(int i=1;i<=n;i++){
		int mi=INT_MAX;
		for(int v:g[i]){
			if(v<mi){qz[i].push_back(v),h[i].sum++,mi=v;}
		}h[i].mi=mi;h[i].id=i;
	}sort(h+1,h+n+1,cmp);
	for(int i=1;i<=n;i++){
		ans+=h[i].sum*n;
		if(h[i].mi>1) ans+=sc(h[i].mi-1);
		getup(h[i].id); 
	}cout<<ans;
	return 0;
}

t3

解法:

code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6,INF=1e18;
int n;
int w[N],b[N],f[N];

inline int dfs(int S){
	if(S==0) return min(b[0],w[0]);
	if(f[S]<INF) return f[S];
	for(int i=0;i<n;i++){
		if(S&(1<<i)){
			int tmp=min(w[S]-w[S^(1<<i)],b[S]-b[S^(1<<i)]);
			f[S]=min(f[S], dfs((S^(1<<i))) + tmp);
		}
	}
	return f[S];
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=0;i<(1<<n);i++) cin>>b[i];
	for(int i=0;i<(1<<n);i++) cin>>w[i];
	for(int j=0;j<n;j++){
		for(int s=0;s<(1<<n);s++){
			if(s&(1<<j)){
				w[s]+=w[s^(1<<j)];
				b[s]+=b[s^(1<<j)];
			} 
		}
	}
	for(int i=0;i<(1<<n);i++) f[i]=INF;
	cout<<dfs((1<<n)-1);
	return 0;
}

t4

想到了70%的正解,但是想麻烦了,所以没写出来,下次写性质题注意一下。

题意:

给定正整数 \(n\) 和两个长度均为 \(n\) 的正整数序列 \(a,b\)

每次你可以选择两个正整数 \(x,y\)(不一定不等),满足 \(x,y\) 都在 \(a\) 中出现过,然后将 \(x\)\(a\) 中第一次出现的位置的值改成 \(y\)

现在你要把 \(a\) 变得和 \(b\) 一样,请给出一种方案,或者判断无解。

解法:

code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e2+10;
int n,a[N],b[N],sum=0,lsta[N],lstb[N];
pair<int,int> ans[N*N];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){cin>>a[i];lsta[a[i]]=i;}
    for(int i=1;i<=n;i++){cin>>b[i];lstb[b[i]]=i;}
    for(int i=1;i<=n;i++){
    	 if((!lsta[i]&&lstb[i])||(lsta[i]&&lstb[i]&&lsta[i]>lstb[i])){
            cout<<"NO";return 0;
        }
	}cout<<"YES\n";
    for(int i=n;i;i--){
        if(a[i]==b[i]) continue;
        for(int j=1;j<=i;j++){
        	if(a[j]==a[i]){
                ans[++sum]={a[j],b[i]};
                a[j]=b[i];
            }
		}      
    }cout<<sum<<'\n';
    for(int i=1;i<=sum;i++){
    	cout<<ans[i].first<<" "<<ans[i].second<<'\n';
	}
}

Conclusion:

估分 实际分 实际做的分 实际会的分
240 170 200 260
posted @ 2025-07-12 14:00  NeeDna  阅读(11)  评论(0)    收藏  举报