南外普及模拟赛

前言

想出了所有题正解,但是愉快挂掉 130pts\operatorname{130pts}


T1

nn 个数,在 [l,r][l,r] 范围内选择一个 kk,使得 nn 个数除以 kk 余数和最小。

1lr3000,1ai30001\leq l\leq r \leq 3000,1\leq a_i \leq 3000


直接暴力枚举。

得分 100100

T2

给定一个数 pp。求最小的 qq 使得 p×qp\times q 乘积后有 kk 个 0。


思路也很简单,利用十进制中 10=2×510=2\times 5 。直接求出这两个质因子的个数。

然后看距离目标需要补多少个。这是赛时代码:

#include<bits/stdc++.h>
using namespace std;
int Q;
long long a,k,c2,c5;
int main() {
    ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
    cin>>Q;
    while(Q--) {
        c2=c5=0;
        cin>>a>>k;
        while(a%2==0) {
            a/=2; c2++;
        }
        while(a%5==0) {
            a/=5; c5++;
        }
        if(min(c2,c5)>=k) cout<<"1\n";
        else {
            long long p1=k-c2,q1=k-c5,p,q;
            //p1,q1 小于 0 时,p,q UB 
            if(p1>=0) p=pow(2,p1);
            if(q1>=0) q=pow(5,q1);
            cout<<p*q<<"\n";
        }
    }
    return 0;
}

这是没有分的,因为当目标与已有质因数差小于 0 时,没有赋初值!!!!

long long p1=k-c2,q1=k-c5,ans=1;
if(p1>=0) ans*=pow(2,p1);
if(q1>=0) ans*=pow(5,q1);

在使用 if 的时候,要考虑两种分支!

挂掉 100pts\operatorname{100pts}


T3

给定一个完全二叉树。每个点上有一个字符。统计有多少个点 uu,使以 uu 为根的子树的所有结点通过任意排列构成回文串。

然后进行 qq 次修改,形如 x k。将点 xx 修改为 kk

1n,q1051\leq n,q \leq 10^5


考虑先搜索一遍,自下而上找到每个值是否合法。使用回文数的性质,如果在 a-z 中有 2\geq 2 个的字符个数为奇数。就不合法。

对于修改,由于最多影响 log2nlog_2 n 个节点,直接暴力改掉。

复杂度 O(26mlog2n)O(26 m\log_2 n)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n,m;
int c[N][30],ans,w[N],t[N];
char s;
void dfs(int from) {
    if(from*2<=n) dfs(from*2);
    if(from*2+1<=n) dfs(from*2+1);
    int k=0;
    for(int i=0;i<26;i++) {//from*2 越界
        c[from][i]+=c[from*2][i]+c[from*2+1][i];
        k+=c[from][i]&1;
    }
    if(k<2) ans++ ,t[from]=1;
}
void chg(int from,int x,int k) {
    while(from) {
        c[from][x]+=k;
        int p=0;
        for(int i=0;i<26;i++) p+=c[from][i]&1;
        if(p>=2&&t[from]==1) ans--,t[from]=0;
        if(p<2&&t[from]==0) ans++,t[from]=1;
        from>>=1;
    }
}
int main() {
    ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
    cin>>n>>m;
    if(n>100000||m>100000) while(1);
    for(int i=1;i<=n;i++) {
        cin>>s;
        c[i][s-'a']=1;
        w[i]=s-'a';
    }
    dfs(1);
    cout<<ans<<"\n";
    while(m--) {
        int x; char y;
        cin>>x>>y;
        chg(x,w[x],-1);
        w[x]=y-'a';
        chg(x,w[x],1);
        cout<<ans<<"\n";
    }
    return 0;
}

由于数组越界,数组也只开了 10510^5,挂掉了 30pts\operatorname{30pts}

解决方法:

  1. 把数组开到 2×1052\times 10^5
  2. 加入判断,防越界。

T4

输入 nnnn44 的倍数。问满足长度为 nn 的顺序对数量等于逆序对数量的字典序最小的序列。


需要足够的发散性思维。

显然长度为 nn 的序列总共有 n(n1)/2n(n-1)/2 个对。而每个数都不相等,所以这些对不是顺序对就是逆序对。所以顺序对,逆序对都有 n(n1)/4n(n-1)/4 个。

我们可以对顺序对进行讨论。从贪心的角度看,1n1\sim n 一定是最优的。但是这样就会使得顺序对过多。

当无法顺着选时,就找一个数,提供剩下的顺序对,使得顺序对数量达到 n(n1)/4n(n-1)/4

再选了这些数后,我们无法选其他的,所以倒序输出剩下的数。

因为字典序的性质,我们可以用贪心解决本题。推起来很简单,但是真的无从下手。对着全排列打的表看了一个小时...


#include<bits/stdc++.h>
using namespace std;
#define rint register unsigned int
unsigned int n;
long long taa,x;
int main() {
    ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
    cin>>n;
    taa=n*(n-1LL)>>2;
    for(rint i=1;i<=n;i++) {
        if(x+n-i<=taa)
            cout<<i<<" ",x+=n-i;
        else {
            cout<<n-(taa-x)<<" ";
            for(rint j=n;j>=i;j--)
                if(j^n-taa+x) cout<<j<<" ";
            return 0;
        }
    }
    return 0;
}
posted @ 2023-10-13 09:32  cjrqwq  阅读(9)  评论(0)    收藏  举报  来源