ZR 2025 模拟赛集

NOIP 10 连测

Day 1

T1

题意:

大概就是说,给你 \(n\) 个数 \(a_1,a_2,a_3 \dots a_n\) ,你可以从中选出若干个数,问能使选出的数平均值为 \(A\) 的方案数。

Solution

根据平均数公式:
(选出 \(m\) 个数,并存在 \(b\) 数组中)

\[\bar{x}=\frac{\sum_{i=1}^m b_i}{m} \]

我们可以枚举 \(A\) 的倍数 \(k*A\),并预处理出来选 \(k\) 个数总和为 \(k*A\) 的方案。

显然是个简单 DP。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,A;
int b[103];
int a[103];
int ans;
int sum[103];
int dp[2503][51]; 
void work(){
    memset(dp,0,sizeof dp);
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=sum[i];j>=a[i];j--){
            for(int k=i;k>=1;k--){
                dp[j][k]+=dp[j-a[i]][k-1];
            }
        }
    }
}
signed main(){
    cin>>n>>A;
    sum[0]=0;
    for(int i=1;i<=n;i++) cin>>a[i],sum[i]=a[i]+sum[i-1];
    work();
    for(int i=A;i<=n*A;i+=A){
        ans+=dp[i][i/A];
    }
    cout<<ans;
}

T2

题意:

给你一张无向图,边有边权,经过一条边的代价为 \(\min(之前经过的边的边权最小值,与这条边的边权值)\)
求从 \(1\)\(n\) 的最小代价。

Solution

首先贪心的想一下,容易想到肯定是先走边权小的边。
所以我们可以先 \(Floyd\) 处理出来两点间的最短路。
然后从最大的边开始枚举,表示以它为当前最小边权,更新 \(1~n\) 的最小代价。

\(Q:\) 为什么要从最大边枚举?

\(A:\) 因为枚举时是设当前枚举边为最小边权,所以之前的边需要比它大。

Code

#include<bits/stdc++.h>
using namespace std;
const int M=125001;
const int N=502;
int n,m;
struct node{
    int u,v,w;
}z[M];
int dis[N][N];
bool cmp(node x,node y){
    return x.w>y.w;
}
int dp[N];
signed main(){
    cin>>n>>m;
    memset(dis,0x3f3f3f3f,sizeof dis);
    for(int i=1;i<=m;i++){
        cin>>z[i].u>>z[i].v>>z[i].w;
        dis[z[i].u][z[i].v]=1;
        dis[z[i].v][z[i].u]=1;
    }
    for(int i=1;i<=n;i++) dis[i][i]=0;
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
            }
        }
    }
    memset(dp,0x3f3f3f3f,sizeof dp);
    sort(z+1,z+1+m,cmp);
    dp[1]=0;
    for(int i=1;i<=m;i++){
        int x=z[i].u,y=z[i].v,w=z[i].w;  
        if(dp[x]>dp[y]) swap(x,y);
        for(int j=1;j<=n;j++){
            dp[j]=min(dp[j],dp[x]+(dis[y][j]+1)*w);
        }
    }
    cout<<dp[n];
}

Day 2

T1

题意:

这里有个数组 \(a\)\(a_1=1,a_2=2,a_3=3 \dots a_n=n\)
定义数组的价值为 \((a_1 \otimes 1)+(a_2 \otimes 2)+(a_3 \otimes 3)+\dots+(a_n \otimes n)\)
你可以交换 \(k\) 次,问最后数组价值最大是多少。

Solution

由于是 \(\otimes\) 所以尽量让 \(a_i\)\(i\) 的二进制不同。
考虑贪心。
发现 \(x\)\(2^m-x-1\) 匹配时最优。

Code

#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
int T,n,k;
int ans;
int PW[1003]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824};//30
void solve(int l,int r){
    if(l==r||r<l||!k) return ;
    for(int i=31;i>=1;i--){
        if(PW[i]<=r){
            int p=min((PW[i]-1),(r-PW[i]+1));
            p=min(p,k);
            ans+=(p*2*(PW[i+1]-1));
            k-=p;
            solve(l,PW[i]-1-min((PW[i]-1),(r-PW[i]+1)));
            return ;
        }
    }
}
void work(){
    ans=0;
    cin>>n>>k;
    solve(1,n);
    cout<<ans<<endl;
}
signed main(){
    cin>>T;
    while(T--) work();
}

T2

题意

就是有个数组 \(a\) ,你可以将 \(a_i\) \(+1\)\(-1\) ,代价为 \(1\)
最后如果有 \(a_i\) = \(a_j+1\) ,那么代价再增加 \(1\)

Solution

容易想到将同值的 \(a_i\) 一起操作。
枚举 \(min_a\)\(max_a\),我们将 \(a_i\) 统一转移到差值为 \(1\)\(a_j\) 上。

Code

#include<bits/stdc++.h>
using namespace std;
int n;
int a;
int ans;
int cnt[100003];
int maxx;
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a,cnt[a]++,maxx=max(maxx,a);
    for(int i=1;i<=maxx;i++){
        int k=min(cnt[i-1],cnt[i]);
        ans+=k;
        cnt[i-1]-=k;
        cnt[i]-=k;
    }
    cout<<ans;
}

Day 3

T1

题意

给你一张有向图,边有边权,点有一定限制,只有在 \([b+kl,e+kl] ,k \in N\) ,求 \(1\)\(n\) 的最短路。

Solution

单源最短路板子

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+20;
const int M=1e6+20;
const int inf=1e18;
int n,m;
struct point{
	int b,e,l;
}p[N];
struct street{
	int y,w;
};
int u,v2,w;
vector<street> v[N];
void init(){
	cin>>n>>m;
	for(int i=2;i<n;i++){
		cin>>p[i].b>>p[i].e>>p[i].l;
	}
	for(int i=1;i<=m;i++){
		cin>>u>>v2>>w;
		v[u].push_back((street){v2,w});
	}
}
bool vis[N];
int dis[N];
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii> > q;
int Len(int x,int t){
	if(x==1||x==n) return 0;
	int k=t%p[x].l;
	if(k>=p[x].b&&k<=p[x].e) return 0;
	else{
		if(k<p[x].b) return p[x].b-k;
		if(k>p[x].e) return p[x].l+p[x].b-k;
	}
}
void djie(){
	for(int i=1;i<=n;i++){
		dis[i]=inf;
		vis[i]=0;
	}
	dis[1]=0;
	q.push({0,1});
	while(!q.empty()){
		int x=q.top().second;
	//	cout<<"x"<<x<<endl;
		q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(auto _:v[x]){
			int y=_.y;
			int w=_.w;
			//cout<<"y"<<y<<" "<<Len(y,dis[x]+w)<<endl;
			if(dis[y]>dis[x]+w+Len(y,dis[x]+w)){
				dis[y]=dis[x]+w+Len(y,dis[x]+w);
				q.push({dis[y],y});
			}
		}
	}
}
signed main(){
	freopen("rgba.in","r",stdin);
	freopen("rgba.out","w",stdout);
	init();
	djie();
	//cout<<Len(3,100)<<"\n";
	cout<<dis[n];
}

T2

题意:

\(2*n\) 个数,每个数小于等于 \(n\),求一个区间使得可划分为两个总和相等的集合。

Solution:

对于每个数,都赋一个权 \(1\)\(-1\) ,这样可以将前缀和控制在 \([-n+1,n]\) 中,根据鸽巢原理,肯定有满足的。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[4000004];
int d;
int k;
int vis[4000004];
signed main(){
    cin>>n;
    n*=2;
    memset(vis,0,sizeof vis);
    for(int i=1;i<=n;i++){
        cin>>k;
        if(d>0){
            d-=k;
        }
        else{
            d+=k;
            vis[i]=1;
        }
        if(d==0||a[d+n]){
            cout<<a[d+n]+1<<" "<<i<<"\n";
            for(int j=a[d+n]+1;j<=i;j++) cout<<vis[j];
            return 0;
        }
        a[d+n]=i;
    }
}

Day 4

T1

题意:

给你一个数组 \(a\) ,可任意选出 \(a_i\)\(a_j\)\(a_i - a_j\) 的绝对值加入 \(a\) 数组(如果之前有这个数就不加)问最后最多会有几个数。

Solution

容易发现,最后的 \(a\) 数组从小到大排后形式为:

\(a_1\)\(2*a_1\) , \(3*a_1\) , $4*a_1 $ \(\cdots\)

可以发现 \(a_1\) 其实就是给出的 \(a\) 数组的最大公约数。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+20;
int gcd(int x,int y){
	if(!y) return x;
	else return gcd(y,x%y);
}
int n;
int T;
int a[N];
void solve(){
	cin>>n;
	int maxx=-1;
	for(int i=1;i<=n;i++) cin>>a[i],maxx=max(maxx,a[i]);
	if(n==1){
		cout<<"1\n";
		return ;
	}
	int GCD=gcd(a[1],a[2]);
	for(int i=2;i<n;i++) GCD=gcd(GCD,a[i+1]);
	cout<<(maxx/GCD)<<"\n";
}
signed main(){
	cin>>T;
	while(T--) solve();
	
}

CSP 7 连测

Day 1

T1

题意:

\(n\) 个人,每个人有个值 \(a\)
给出每个人认识的人。
对于第 \(i\) 个人,他若认识 \(m\) 个人,其中 \(k\) 个人的值比他大。
如果 \(k\ge \frac{m}{2}\) 那么这个人就会放弃。
问有多少人会放弃。

Solution

暴力即可

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int c,a,b;
int q[20000004];
int x;
int sum;
int ans;
int vis[20000004];
int cnt;
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>q[i];
    for(int i=1;i<=n;i++){
        cin>>c>>a>>b;
        sum=0,cnt=0;
        x=b;
        for(int j=1;j<=c;j++){
            if(vis[x]!=i&&x!=i) vis[x]=i,cnt++;
            else{
                x=(x*a+b)%n+1;
                continue;
            }
            sum+=(q[x]>q[i]);
            x=(x*a+b)%n+1;
        }
        ans+=(sum*2>=cnt&&(sum!=0));
    }
    cout<<ans;
}

T2

题意:

给你个数,在后面加数,问最小可得的质数

Solution:

由于 \(1000\) 个数中必有质数,所以暴力即可。

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int q;
const int inf=100000000;

int prime[10000005];
bool vis[100000003];
int tot;
ll ans[1000003];
void XX(int n){
    memset(vis,1,sizeof vis);
    vis[1]=0;
    for(int i=2;i<=n;i++){
        if(vis[i]){
            prime[++tot]=i;
        }
        for(int j=1;j<=tot&&prime[j]*i<=n;j++){
            vis[prime[j]*i]=0;
            if(i%prime[j]==0) break;
        }
    }
}
inline bool check(ll x){
    if(x<=inf) return vis[x];
     for(int i=2;i*i<=x;i++){
        if(x%i==0) return 0;
    }
    return 1;
}
void work(int x){
    if(check(x)){
        ans[x]=x;
        return ;
    }
        bool flag=0;
        for(int i=1;i<=9;i+=2){
            ll y=x*10+i;
            if(check(y)){
                ans[x]=y;
                return ;
            }
        }
        for(int i=0;i<=9;i++){
            for(int j=1;j<=9;j+=2){
                ll z=x*100+i*10+j;
                 if(check(z)){
                    ans[x]=z;
                    return ;
                }            
            }
        }
        for(int i=0;i<=9;i++){
            for(int j=0;j<=9;j++){
                for(int k=1;k<=9;k+=2){
                    ll p=x*1000+i*100+j*10+k;
                     if(check(p)){
                        ans[x]=p;
                        return ;
                    }
                }
            }
        }
}
signed main(){
    ios::sync_with_stdio(false);   
    XX(inf);
   for(int i=1;i<=1000000;i++){
        work(i);
   }
    cin>>q;
    while(q--){
        int x;
        cin>>x;
        cout<<ans[x]<<"\n";
    }
}

T3

题意:

有个 \(a\) 数组,\(a_1=1,a_2=2 \dots a_n=n\)
\(n\) 个人,每个人对应着 \(a\) 数组中的值。
\(m\) 个要求。
每个要求给 \(p\)\(x\),表示下标大于 \(p\) 的人中,\(a\) 的值大于第 \(p\) 个人的人恰好有 \(x\) 个。

Solution

可以发现,对于一个要求,符合的数是个区间,而每次贪心取最大的即可。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
int T;
int n,m;
struct node{
    int l,r,sum;
}z[4*N];
struct cjx{
    int p,x;
}q[N];
int X[N];
void build(int l,int r,int x){
    z[x].l=l;
    z[x].r=r;
    if(l==r){
        z[x].sum=1;
        return ;
    }
    int mid=l+r>>1;
    build(l,mid,x<<1);
    build(mid+1,r,x<<1|1);
    z[x].sum=z[x<<1].sum+z[x<<1|1].sum;
}
int query(int k,int x){
    if(z[x].l==z[x].r) return z[x].l;
    if(k>z[x<<1].sum) return query(k-z[x<<1].sum,x<<1|1);
    else return query(k,x<<1);
}
void del(int k,int x){
    if(z[x].l==z[x].r){
        z[x].sum=0;
        return ;
    }
    int mid=z[x].l+z[x].r>>1;
    if(k<=mid) del(k,x<<1);
    else del(k,x<<1|1);
    z[x].sum=z[x<<1].sum+z[x<<1|1].sum;
}
int ans[N];
signed  main(){
    int o=0;
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--){
        cin>>n>>m;
        o++;
        build(1,n,1);
        bool flag=0;
        for(int i=1;i<=n;i++) X[i]=-1;
        for(int i=1;i<=m;i++){
            cin>>q[i].p>>q[i].x;
            X[q[i].p]=q[i].x;
            if(n-q[i].p-q[i].x+1<=0){
                flag=1;
            }
        }
        if(flag){
            cout<<"-1\n";
            continue;
        }
        for(int i=1;i<=n;i++){
            if(X[i]==-1){ 
                if(n-i+1<=0){
                    flag=1;
                    break;
                }
               ans[i]=query(n-i+1,1);
            }
            else{ 
                if(n-i-X[i]+1<=0){
                    flag=1;
                    break;
                }
                ans[i]=query(n-i-X[i]+1,1);
            }
            del(ans[i],1);
        }
        if(flag){
            cout<<"-1\n";
            continue;
        }
        for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
        cout<<"\n";
    }
}

Day 2

T2

题意:

给你 \(n\) 个数,你需要将它们划分为 \(3\) 个集合,满足第 \(1\) 集合的最大值小于等于第 \(2\) 个集合的长度,第 \(2\) 个集合的最大值小于等于第 \(3\) 个集合的长度,第 \(3\) 个集合的最大值小于等于第 \(1\) 个集合的长度。

Solution:

贪心,但要注意把情况算全。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int T;
int n;
int col[N];
struct node{
    int id,x;
}a[N];
bool cmp(node x,node y){
    return x.x>y.x;
}
signed main(){
    ios::sync_with_stdio(false);
    //freopen("performance.in","r",stdin);
   // freopen("performance.out","w",stdout);
    cin>>T;
    while(T--){
        cin>>n;
        //cout<<n<<endl;
        for(int i=1;i<=n;i++) {
            cin>>a[i].x;
            a[i].id=i;
        }
        sort(a+1,a+1+n,cmp);
        bool flag=0;
       // for(int i=1;i<=n;i++) cout<<a[i]<<" ";
       // cout<<endl;
        for(int i=1;i<=n-2;i++){
            int l=n-a[1].x+1,r=n;
            if(l-i<=1) continue;
           // cout<<i<<" "<<(l-1-(i+1)+1)<<" "<<n-l+1<<" "<<a[i+1]<<" "<<a[l]<<endl;
          //  cout<<i<<" "<<l<<" "<<r<<endl;
             //cout<<(a[l]<=n-r)<<" I i " <<i<<" a[l] "<<a[l]<<" r "<<r<<" n-r "<<n-r<<" r-l+1 "<<r-l+1<<" a[i] "<<a[i]<<" l "<<l<<" a[r+1] "<<a[r+1]<<endl;
        //    cout<<(a[l]>=(l-1-(i+1)+1))<<" "<<((a[i+1]>=i))<<"\n";
            if(a[l].x<=(l-1-(i+1)+1)&&(a[i+1].x<=i)){
           //     cout<<a[l]<<" "<<r<<" "<<n-r<<" "<<r-l+1<<" "<<a[i]<<" "<<l<<" "<<a[r+1]<<endl;
                flag=1;
                cout<<"YES\n";
                for(int j=1;j<=i;j++) col[a[j].id]=1;
                for(int j=i+1;j<=l-1;j++) col[a[j].id]=3;
                for(int j=l;j<=r;j++) col[a[j].id]=2;
                for(int j=1;j<=n;j++) cout<<col[j]<<" ";
                cout<<"\n";
                break;
            }
            l=i+1,r=l+a[1].x-1;
            if(r>=n||l>=n) continue;
            if(a[r+1].x<=i&&a[l].x<=(n-(r+1)+1)){
                flag=1;
                cout<<"YES\n";
                for(int j=1;j<=i;j++) col[a[j].id]=1;
                for(int j=l;j<=r;j++) col[a[j].id]=2;
                for(int j=r+1;j<=n;j++) col[a[j].id]=3;
                for(int j=1;j<=n;j++) cout<<col[j]<<" ";
                cout<<"\n";
                break;
            } 
        }
        if(flag) continue;
        cout<<"NO\n";
    }
}

T4

题意:

有一个 \(n\) 个点的树,每个点有点权。有 \(q\) 次询问,每次给定一个 \(x\),问是否存在两个祖孙关系的节点 \(u\) , \(v\) 的路径上点权和为 \(x\)

Soltuion:

bitset

Code:

#include<bits/stdc++.h>
using namespace std;
int n,q;
int f;
vector<int> v[1000003];
int a[2000004];
bitset<100050> Q,b[41];
int x[1000004];
bool vis[1000003];
int rt;
struct node{
    int son,maxson,fa;
}z[1000004];
void dfs1(int x,int fa){
    z[x].fa=fa;
    z[x].maxson=0;
    z[x].son=1;
    for(auto y:v[x]){
        if(y!=fa){
            dfs1(y,x);
            z[x].son+=z[y].son;
            if(z[y].son>z[z[x].maxson].son){
                z[x].maxson=y;
                z[z[x].maxson].son=z[y].son;
            }
        }
    }
}
void dfs2(int x,int now){
    b[now].set(0,1);
    b[now]<<=a[x];
    Q^=(Q&b[now]);
    for(auto y:v[x]){
        if(y!=z[x].fa&&y!=z[x].maxson){
            b[now+1]=b[now];
            dfs2(y,now+1);
        }
    }
   if(z[x].maxson) dfs2(z[x].maxson,now);
}
signed main(){
    cin>>n>>q;
    int rt;
    for(int i=1;i<=n;i++){
        cin>>f;
        vis[i]=1;
        
        if(!f) rt=i;
        else v[f].push_back(i);
    }
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=q;i++){
        cin>>x[i];
        Q.set(x[i],1);
    }
    dfs1(rt,0);
    dfs2(rt,0);
    for(int i=1;i<=q;i++){
        if(Q[x[i]]==1) cout<<"NO\n";
        else cout<<"YES\n";
    }
}

Day 3

T1

唐题

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,m;
int K;
const int mod=1e9+7;
int ans;
int mp[10];
int xh[4]={0,1,4,2};
void work(int x){
    if(x==1||x==2||x==4||!m){
        a=x;
        return ;
    }
        ans=(ans+x)%mod;
    if(x%2==0) --m,work(x/2);
    else --m,work(3*x+1);
}
signed main(){
    cin>>a>>m;
    work(a);
    int k=m/3;
    ans=(ans+(k*7)%mod)%mod;
    m%=3;
    mp[1]=1,mp[4]=2,mp[2]=3;
    if(m){
        int x=mp[a];
        while(m){
            ans=(ans+xh[x])%mod;
            if(x==3) x=1;
            x++,m--;
        }
    }
    cout<<ans;
}

T2

题意:

posted @ 2025-09-12 11:21  Distant_Cloud  阅读(9)  评论(0)    收藏  举报