Aug. 2023 普及组模拟赛 2

题面

T1 地址

Meaning

给定一个字符串,判断是否合法。若不合法,则将其变为合法字符串。

Solution

情况较少,直接枚举每个字符判断。实现时应注意:

  1. 题目强调了数据保证字符串中存在且仅存在 \(4\) 个被字符分开的整数,也就是说,可以利用数的连续性缩小分支结构。
  2. 注意判断是否所有的 . 都合法,放在最后不合法。
代码
#include<bits/stdc++.h>
using namespace std;
char a[100],ans[100];
int n,tem,cnt;
bool flag;
stack<int> st;
inline void pus(int x){
    while(x%10!=x){
        st.push(x%10);
        x=x/10;
    }
    ++cnt;
    ans[cnt]=x+'0';
    while(!st.empty()){
        ++cnt;
        ans[cnt]=st.top()+'0';
        st.pop();
    }
}
int main(){
    freopen("ip.in","r",stdin);
    freopen("ip.out","w",stdout);
    scanf("%s",a);
    int n=strlen(a);
    for(int i=0;i<n;++i){
        if(a[i]>='0'&&a[i]<='9'){
            if(a[i]=='0'&&(!tem)) flag=1;
            while(a[i]>='0'&&a[i]<='9'){
                tem=tem*10+a[i]-'0';
                ++i;
            }
            --i;
            if(tem>255){
                flag=1;
                pus(255);
                tem=0;
            }else{
                pus(tem);
                tem=0;
            }
            ++cnt;
            ans[cnt]='.';
        }else if(a[i]!='.'){
            flag=1;
        }
    }
    if(a[n-1]=='.') flag=1; 
    if(flag==1) printf("NO\n");
    else return printf("YES\n"),0;
    for(int i=1;i<cnt;++i) printf("%c",ans[i]);
    return 0;
}

T2 内积

Meaning

给定两个数组,使两个不同数组数组中的元素相乘,求所有元素求积后积的最大和。

Solution

可以看作是一个数组表示一组值,另一个数组表示贡献。对于每个值,从贡献数组中匹配一个该值的贡献。可得对于越大的值,我们希望它做的贡献也越大。
可以将两个数组按相同的方式(同为升序或同为降序)排序,让两数组中同为第 \(k\) 大的数相乘求积之和。

代码
#include<bits/stdc++.h>
using namespace std;
long long n,a[2000000],b[2000000],ans;
int main(){
    freopen("nj.in","r",stdin);
    freopen("nj.out","w",stdout);
    scanf("%lld",&n);
    for(int i=0;i<n;++i) scanf("%lld",&a[i]);
    for(int i=0;i<n;++i) scanf("%lld",&b[i]);
    sort(a,a+n);
    sort(b,b+n);
    for(int i=0;i<n;++i) ans+=a[i]*b[i];
    printf("%lld",ans);
    return 0;
}

T3 翻转

Meaning

给定一个只含 \(0,1\) 的矩阵,每次可以改变一个位置和它上,下,左,右四个位置上的值,求是否可以使该矩阵上的所有值变为相同的数。若可以,则求出所需最小的改变次数。

Solution

数据范围很小,可以考虑深搜或枚举。
假设要把所有值变为 \(y\),且有

\[\operatorname{opp}_i= \begin{cases}0 &(i=1)\\1&(i=0) \end{cases} \]

对于矩阵 \(a\),如果 \(a_{i,j}\ne{y}(i\ne{n})\),那么为了保证这一行的状态,使 \(a_{i,j+1}\leftarrow{\operatorname{opp}_{a_{i,j+1}}}\)\(a_{i,j-1}\leftarrow{\operatorname{opp}_{a_{i,j-1}}}\) 会不断破环这一行的某个元素,就需要使 \(a_{i+1,j}\leftarrow{\operatorname{opp}_{a_{i+1,j}}}\)。以此类推,到最后一行时,如果还存在 \(a_{n,i}\ne{y}\) 则因继续改变 \(a_{n,i}\) 需要破坏上方状态而可以断定这种状态下不存在合法改变方式。
下图展示了一种情况:
可以用深搜枚举第一列的状态,复杂度为 \(O(2^n)\),再进行上述操作,复杂度为 \(O(n^2)\)

代码
#include<bits/stdc++.h>
using namespace std;
bool a[20][20],tem[20][20];
char ch;
int n,minn=0x3f3f3f3f,dir[2][5]={{0,0,0,1,-1},{0,1,-1,0,0}};
inline void flip(int x,int y){
    for(int i=0;i<5;++i){
        if(tem[x+dir[0][i]][y+dir[1][i]]==1) tem[x+dir[0][i]][y+dir[1][i]]=0;
        else tem[x+dir[0][i]][y+dir[1][i]]=1;
    }
}
inline void act(int* x,int y){
    int step=0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j) tem[i][j]=a[i][j];
    }
    for(int i=1;i<=n;++i){
        if(tem[1][i]!=x[i]){
            flip(1,i);
            ++step;
        }
    }
    for(int i=1;i<n;++i){
        for(int j=1;j<=n;++j){
            if(tem[i][j]!=y){
                ++step;
                flip(i+1,j);
            }
        }
    }
    for(int i=1;i<=n;++i) if(tem[n][i]!=y) return;
    minn=min(minn,step);
}
inline void dfs(int* arr,int x,int y){
    if(x>n){
        act(arr,1);
        act(arr,0);
        return;
    }
    int *c=arr;
    c[x]=y;
    dfs(c,x+1,1);
    dfs(c,x+1,0);
}
int main(){
    freopen("fz.in","r",stdin);
    freopen("fz.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            ch=getchar();
            while(ch<'a'||ch>'z') ch=getchar();
            if(ch=='b') a[i][j]=1;
            else a[i][j]=0;
        }
    }
    int b[20];
    dfs(b,1,1);
    dfs(b,1,0);
    if(minn==0x3f3f3f3f) printf("Impossible");
    else printf("%d",minn);
    return 0;
}

T4 阶乘

Meaning

给定一个数 \(n\),求满足 \(\frac{a!}{b!}=n\) 的数对 \((a,b)\) 的个数。

Solution

枚举复杂度过高,需要优化。
由于 \(\frac{a!}{b!}=\begin{matrix} \prod_{i\in[a+1,b]} i \end{matrix}\),所以当 \(len=b-a\) 时,可得:

  1. \(a^{len}<n\),即 \(a<\sqrt[len]{n}\),因为当 \(i\in[a+1,b]\) 时,\(a<i\)
  2. \(b^{len}\geq{n}\),即 \(b\geq\sqrt[len]{n}\),因为当 \(i\in[a+1,b]\) 时,\(b\geq{i}\)
  3. \(1\leq{len}<20\),根据数据范围即可得到(\(10^{18}<20!\))。
    我们可以根据以上结论直接优化枚举。
代码
#include<bits/stdc++.h>
using namespace std;
long long cases,n;
priority_queue<pair<long long,long long>,vector<pair<long long,long long> >,greater<pair<long long,long long> > > pq;
inline long long read(){
    char ch=getchar();
    long long x=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
inline long long binpow(long long x,long long y){
    long long ans=1;
    while(y>0){
        if(y&1==1) ans*=x;
        x*=x,y>>=1;
    }
    return ans;
}
int main(){
    freopen("jc.in","r",stdin);
    freopen("jc.out","w",stdout);
    cases=read();
    while(cases--){
        n=read();
        if(n==1){
            printf("-1\n");
            continue;
        }
        pq.push({n,n-1});
        for(long long l=2;l<20;++l){
            for(long long i=powl(n,(double)1/l)-l;i<=powl(n,(double)1/l);++i){
                if(i<=0) continue; 
                int j=i+l;
                long long tem=1;
                for(int k=i+1;k<=j;++k){
                    tem*=k;
                    if(tem>n) goto placea; 
                }
                if(tem==n) pq.push({j,i});
                placea:;
            }
        }
        printf("%lld\n",pq.size());
        while(!pq.empty()){
            printf("%lld %lld\n",pq.top().first,pq.top().second);
            pq.pop();
        }
    }
    return 0;
}
posted @ 2023-08-19 20:27  Pursuing_OIer  阅读(17)  评论(0)    收藏  举报