2022年4月校赛总结、题目和题解

总结

本次校赛总体看,总体难度相对去年略有降低。总体榜单大致符合出题人预期。
问题:出题人对签到题把控失误(C语言读多行char型,吸收回车问题,也可能是从polygon上下载了windows的数据导致换行为\r\n而不是\n)。
下次4月赛应该出类型不同的两个签到题(例如一个输入输出思维和一个if-else)。
感谢辽宁大学Cantor_提供的一道题和参与验题,也感谢辽大同学前来捧场。

题目

题目:http://dluacm.cn/wp-content/uploads/2022/04/202204zhengshi.pdf
补题:https://ac.nowcoder.com/acm/contest/32275

题解

Solution A(数据泄露):

因为数据范围只有500,且可能包含重边、自环和不连通,所以很容易想到flyod算法。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define imax 0x3f3f3f3f
#define lmax 0x3f3f3f3f3f3f3f3f

ll num[1005][1005];
int main(){

    ll n,m;
    cin>>n>>m;

    memset(num,0x3f,sizeof num);
    for(int i=1;i<=n;i++){
        num[i][i]=0;
    }
    for(int i=0;i<m;i++){
        ll a,b,s;
        cin>>a>>b>>s;
        num[a][b]=min(num[a][b],s);
        num[b][a]=min(num[b][a],s);
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(num[i][k]<1e9&&num[k][j]<0x3f3f3f3f){
                    num[i][j]=min(num[i][j],num[i][k]+num[k][j]);
                }
            }
        }
    }
    int k;
    cin>>k;
    int start=1;
    ll re=0;
    for(int i=0;i<k;i++){
        int p;
        cin>>p;
        if(re==-1) continue;
        if(num[start][p]>1e9){
            re=-1;
            continue;
        }
        re+=num[start][p];
        start=p;
    }
    cout<<re<<endl;
    return 0;
}

Solution B(波格丹危机):

签到题,按照题意排序即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define imax 0x3f3f3f3f
#define lmax 0x3f3f3f3f3f3f3f3f
int main(){

    int n;
    cin>>n;
    vector<pair<int,string>> v;
    while(n--){
        string s;
        int a;
        cin>>s>>a;
        v.push_back({a,s});
    }
    sort(v.begin(),v.end());
    for(int i=0;i<v.size();i++){
        if(v[i].second=="."||v[i].second=="!") cout<<v[i].second<<endl;
        else cout<<v[i].second<<" ";
    }
    return 0;
}

Solution C(末日将至):

首先,如果发生全反射,说明一定能照射到全部数据包。

如果不能,当光照射在两个挡板交界处时,我们需要判断是否有数据包在光上面。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define imax 0x3f3f3f3f
#define lmax 0x3f3f3f3f3f3f3f3f
int main(){

    int T;
    cin>>T;
    while(T--){
        int a,b,n;
        double k;
        cin>>a>>b>>n>>k;
        pair<int,int> nums[n];
        for(int i=0;i<n;i++){
            cin>>nums[i].first>>nums[i].second;
        }
        double tana=(double)b/(double)a;
        double ana=atan(tana);
        double sina=sin(ana);
        double sinb=sina/k;
        bool c=0;
        if(fabs(sinb-1.0)>0.000001&&sinb>1.0){
            c=1;
        }
        if(c==1){
            cout<<"YES"<<endl;
            continue;
        }
        double k2=tan(asin(sinb));
        double b2=b-k2*a;
        bool f=1;
        for(int i=0;i<n;i++){
            double y1=k2*nums[i].first+b2;
            if(fabs(nums[i].second-y1)>0.000001&&nums[i].second>y1){
                f=0;
            }
        }
        if(f==1) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;

    }
    return 0;
}

Solution D(Raksasa的摸der):

本题考虑对多种情况的判断。

例如:d ,de 会对后面的r的贡献产生影响。算区间的时候不能把前面的d,de考虑进去。

解题思路是按照多种情况考虑,算清楚所有情况即可。运用前缀和计数。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x7ffffffffffffff
#define N 1005000
#define mod 1000000007
#define mod2 998244353
#define ll long long
char c[N];
ll der[N][3],er[N][2],r[N];
int main()
{
    ll n,x;
    scanf("%lld%lld",&n,&x);
    scanf("%s",c+1);
    ll ans=0;
    for(int i=1; i<=n; i++)
    {
        if(c[i]=='d')
        {
            der[i][0]=der[i-1][0]+1;
            der[i][1]=der[i-1][1];
            der[i][2]=der[i-1][2];
            er[i][0]=er[i-1][0];
            er[i][1]=er[i-1][1];
            r[i]=r[i-1];
        }
        else if(c[i]=='e')
        {
            der[i][0]=der[i-1][0];
            der[i][1]=der[i-1][1]+der[i-1][0];
            der[i][2]=der[i-1][2];
            er[i][0]=er[i-1][0]+1;
            er[i][1]=er[i-1][1];
            r[i]=r[i-1];
        }
        else if(c[i]=='r')
        {
            der[i][0]=der[i-1][0];
            der[i][1]=der[i-1][1];
            der[i][2]=der[i-1][2]+der[i-1][1];
            er[i][0]=er[i-1][0];
            er[i][1]=er[i-1][0]+er[i-1][1];
            r[i]=r[i-1]+1;
        }
        else
        {
            der[i][0]=der[i-1][0];
            der[i][1]=der[i-1][1];
            der[i][2]=der[i-1][2];
            er[i][0]=er[i-1][0];
            er[i][1]=er[i-1][1];
            r[i]=r[i-1];
        }
    }
    for(int i=x;i<=n;i++)
    {
        ans=max(ans,der[i][2]-der[i-x][2]-der[i-x][0]*(er[i][1]-er[i-x][1]-er[i-x][0]*(r[i]-r[i-x]))-der[i-x][1]*(r[i]-r[i-x]));
    }
    printf("%lld\n",ans);
    return 0;
}

也可以按区间一位一位算,这里给出验题人(Cantor.)的代码

#include<bits/stdc++.h>
#define oo 0x3f3f3f3f
#define OO 0x3f3f3f3f3f3f3f3f
#define LL long long
#define sz(x) int(x.size())
#define PII pair<int,int>
#define VI vector<int>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define STP system("pause")
using namespace std;
const int N=1e6;
void Solve(){
    int n,m;
    string s;
    cin>>n>>m>>s;
    vector<LL> f(6);
    //f0 = 'd' f1 = 'e' f2 = 'r' f3 = 'de' f4 = 'er' f5 = 'der'
    auto Add=[&](char x){
        if(x=='d')  f[0]+=1;
        if(x=='e')  f[1]+=1,f[3]+=f[0];
        if(x=='r')  f[2]+=1,f[4]+=f[1],f[5]+=f[3];
    };
    auto Delete=[&](char x){
        if(x=='d')  f[0]-=1,f[3]-=f[1],f[5]-=f[4];
        if(x=='e')  f[1]-=1,f[4]-=f[2];
        if(x=='r')  f[2]-=1;
    };
    auto Print=[&](){
        for(int i=0;i<6;++i)
            cout<<f[i]<<" \n"[i==5];
    };
    for(int i=0;i<m;++i){
        Add(s[i]);
    }
    LL ans=f[5];
    for(int i=m;i<n;++i){
        Delete(s[i-m]);
        Add(s[i]);
        ans=max(ans,f[5]);
    }
    cout<<ans<<'\n';
}
int main(){
    Solve();
}

Solution E(数圆圈):

对于任意1e18以内的数,我们迭代几十次以后都会变成0-1-0-1的循环。

所以我们迭代出0或1以后直接算就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define imax 0x3f3f3f3f
#define lmax 0x3f3f3f3f3f3f3f3f

ll f(ll num){
    string s=to_string(num);
    ll re=0;
    for(int i=0;i<s.size();i++){
        if(s[i]=='6'||s[i]=='9'||s[i]=='0')re++;
        if(s[i]=='8')re+=2;
    }
    return re;
}
int main() {
    int T;
    cin>>T;
    while(T--){
        ll n,k;
        cin>>n>>k;
        ll re=n;
        while(k){
            re=f(re);
            k--;
            if(re==0||re==1) break;
        }
        if(k%2==1){
            if(re==0) re=1;
            else re=0;
        }
        cout<<re<<endl;

    }
    return 0;
}

solution F(旅行):

考虑一个点在1-n某一条最短路径的条件,我们设dis(i,j)表示i到j的最短路径,如果点x在1-n的最短路径上,那么一定有dis(1,x)+dis(x,n)=dis(1,n),考虑到数据只有只有10^3级别,所以我们可以从1-n每个点出发跑一遍堆优化dijkstra,对于输入的x直接判断,时间复杂度O((n+m)^2log(n+m)+m)。

​ 题解采用了另外一种做法。先正常建边以1号点为起点跑堆优化dijkstra,得到1到其他点的最短路径数组dis,然后建一张反图(如果存在边a->b,在反图中存在边b->a),以n号点为起点跑堆优化dijkstra,得到n到其他点的最短路径数组dis1,则对于输入的x,我们直接判断dis(1,x)+dis1(n,x)是否等于dis(1,n)即可,时间复杂度O((n+m)log(n+m)+m)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 2147483647
#define N 100005
#define M 200005
using namespace std ;
struct Edge { int nxt,to,dist ; } e[M],e1[M] ;
struct node {
    int id,d ;
    bool operator <(const node &a) const {
        return d>a.d ;
    }
};
int head[N],head1[N],tot1=0,tot=0 ;
int n,m,S ;
int dis[N],dis1[N] ;
bool vis[N] ;
priority_queue<node> q ;
inline void add(int from,int to,int dist) {
    e[++tot].to=to ; e[tot].dist=dist ;
    e[tot].nxt=head[from] ; head[from]=tot ;
}
inline void add1(int from,int to,int dist) {
    e1[++tot1].to=to ; e1[tot1].dist=dist ;
    e1[tot1].nxt=head1[from] ; head1[from]=tot1 ;
}
void dijkstra(int S) {
    fill(vis+1,vis+n+1,0) ;
    fill(dis+1,dis+n+1,INF) ;
    dis[S]=0 ; q.push((node){S,0}) ;
    while(!q.empty()) {
        node x=q.top() ; q.pop() ;
        if(vis[x.id]) continue ;
        vis[x.id]=1 ;
        for(int i=head[x.id];i;i=e[i].nxt) {
            int y=e[i].to ;
            if(dis[y]>dis[x.id]+e[i].dist) {
                dis[y]=dis[x.id]+e[i].dist ;
                if(!vis[y]) q.push((node){y,dis[y]}) ;
            }
        }
    }
}
void dijkstra1(int S) {
    fill(vis+1,vis+n+1,0) ;
    fill(dis1+1,dis1+n+1,INF) ;
    dis1[S]=0 ; q.push((node){S,0}) ;
    while(!q.empty()) {
        node x=q.top() ; q.pop() ;
        if(vis[x.id]) continue ;
        vis[x.id]=1 ;
        for(int i=head1[x.id];i;i=e1[i].nxt) {
            int y=e1[i].to ;
            if(dis1[y]>dis1[x.id]+e1[i].dist) {
                dis1[y]=dis1[x.id]+e1[i].dist ;
                if(!vis[y]) q.push((node){y,dis1[y]}) ;
            }
        }
    }
}
int main() {
    scanf("%d%d%d",&n,&m,&S) ;
    for(int i=1,u,v,w;i<=m;++i) {
        scanf("%d%d%d",&u,&v,&w) ;
        add(u,v,w) ; add1(v,u,w) ;
    }
    dijkstra(1) ;
    dijkstra1(n) ;
    while(S--)
    {
        int x ;
        scanf("%d",&x) ;
        if(1LL*dis[x]+dis1[x]==1LL*dis[n])
            printf("yes\n") ;
        else printf("no\n") ;
    }
    return 0 ;
}

Solution G(全体目光看向我,我宣布个事):

单纯的统计,实在不行也可以开26个变量来计数。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x7ffffffffffffff
#define N 1005000
#define mod 1000000007
#define mod2 998244353
#define ll long long
ll buc[N];
int main()
{
    ll n;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        char ch;
        scanf(" %c",&ch);
        buc[ch]++;
    }
    ll ans=0;
    char ch;
    for(int i='A';i<='Z';i++)
    {
        if(ans<buc[i])
        {
            ans=buc[i];
            ch=i;
        }
    }
    printf("%c",ch);
    return 0;
}

Solution H(寻找最大差值):

我们创建两个数组,第一个数组是从下标0到i的最小值,第二个数组是从最后面到i的最大值。

然后遍历这两个数组,使用最大值减去最小值即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define imax 0x3f3f3f3f
#define lmax 0x3f3f3f3f3f3f3f3f
int main(){

    int T;
    cin>>T;
    while(T--){
        int n;
        cin>>n;
        int num[n];
        for(int i=0;i<n;i++){
            cin>>num[i];
        }
        deque<int> mx;
        deque<int> mi;
        for(int i=0;i<n;i++){
            if(i==0) mi.push_back(num[0]);
            else{
                mi.push_back(min(mi.back(),num[i]));
            }
        }
        for(int i=n-1;i>=0;i--){
            if(i==n-1) mx.push_front(num[n-1]);
            else mx.push_front(max(num[i],mx.front()));
        }
        int re=-1;
        for(int i=0;i<n;i++){
            if(mx[i]>mi[i])re=max(re,mx[i]-mi[i]);
        }
        cout<<re<<endl;
    }
    return 0;
}

另一份代码:

#include <bits/stdc++.h>
using namespace std;
#define inf 0x7ffffffffffffff
#define N 1005000
#define mod 1000000007
#define mod2 998244353
#define ll long long
ll a[N];
int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        ll n;
        scanf("%lld",&n);
        ll num=0;
        ll ans=-1;
        for(int i=0;i<n;i++) scanf("%lld",&a[i]);
        for(int i=n-1; i>=0; i--)
        {
            num=max(a[i],num);
            if(num-a[i]>0) ans=max(num-a[i],ans);
        }
        printf("%lld\n",ans);
    }

    return 0;
}

solution I(彩虹) :

7种颜色染格子,要求相邻格子颜色不相同,求染色的方案数。

​ 经典状压dp。

​ 我们令dp(i,j)表示第i行状态为j的染色方案数,其中状态j模拟一个n位7进制数,第k位为0-6分别代表格子(i,k)上的颜色为第1-7种颜色,则有dp转移方程
f[i][j]=∑f[i-1][k] \quad if \quad j和k没有颜色重叠

​ 答案即为
∑f[m][i] \quad 0\le i< 7^n
​ 但是本题要注意处理一下一行的合法颜色,不然会TLE

#include<bits/stdc++.h>
#define N 65536
#define mod 1000000007
#define LL long long
using namespace std ;
int q[N],n,m,num=0 ;
LL f[15][N] ;
bool check(int len,int x,int y)
{
    int s,t ;
    while(len--)
    {
        s=x%7,t=y%7 ;
        if(s==t) return false ;
        x/=7 ; y/=7 ;
    }
    return true ;
}
int main()
{
    cin>>n>>m ;
    int res=1 ;
    for(int i=1;i<=n;++i) res*=7 ;
    for(int i=0;i<res;++i)
    {
        int len=n,s=i/7,lst=i%7 ;
        bool flag=1 ;
        while(--len)
        {
            if(s%7==lst) 
            {
                flag=0 ;
                break ;
            }
            lst=s%7 ; s/=7 ;
        }
        if(flag) q[++num]=i ;
    }
    memset(f,0,sizeof(f)) ;
    for(int i=1;i<=num;++i) f[1][i]=1 ;
    for(int i=2;i<=m;++i)
    {
        for(int j=1;j<=num;j++)
            for(int k=1;k<=num;++k)
                if(check(n,q[j],q[k]))
                    f[i][j]=(f[i][j]+f[i-1][k])%mod ;
    }
    LL ans=0 ;
    for(int i=1;i<=num;++i)
        ans=(ans+f[m][i])%mod ;
    cout<<ans<<endl ;
    return 0 ;
} 

Solution J(突然的自我):

签到题,根据题意,很容易推出f(i)=f(i-1)+f(i-2)+1。

Solution K(Raksasa的轻功):

因为跳法的原因,可以判断出具有传递性。

提供两种思路:

一种是判断高低,记录每一座山峰往左和往右能跳到的最低山峰位置

#include <bits/stdc++.h>
using namespace std;
#define inf 0x7ffffffffffffff
#define N 1005000
#define mod 1000000007
#define mod2 998244353
#define ll long long
ll a[N];
ll pre[N],suf[N];
int main()
{
    ll n;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    ll mark=1;
    for(int i=1;i<=n;i++)
    {
        if(a[i]>a[i-1]) pre[i]=mark;
        else pre[i]=mark=i;
    }
    mark=n;
    for(int i=n;i>=1;i--)
    {
        if(a[i]>a[i+1]) suf[i]=mark;
        else suf[i]=mark=i;
    }
    ll ans=0;
    for(int i=1;i<=n;i++) ans=max({ans,i-pre[i],suf[i]-i});
    printf("%lld\n",ans);
    return 0;
}

另一种是直接从左跑到右,从右跑到左,各跑一遍。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x7ffffffffffffff
#define N 1005000
#define mod 1000000007
#define mod2 998244353
#define ll long long
ll a[N];
int main()
{
    ll n;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    ll up=0,down=0;
    ll ans=0;
    for(int i=1;i<n;i++)
    {
        if(a[i]<a[i+1])
        {
            up++;
            down=0;
        }
        else if(a[i]>a[i+1])
        {
            down++;
            up=0;
        }
        else
        {
            up=0;
            down=0;
        }
        ans=max({ans,up,down});
    }
    printf("%lld\n",ans);
    return 0;
}

solution L(函数求和):

一句话题意:
\sum_{i=1}^n min_{L_i<=j<=i}\left{gcd(i,j)\right} \mod (10^9+7)
​ 显然
gcd(i,i-1)=1,gcd(i,i)=i
​ 所以


f(i)=\left{\begin{aligned}\ 1 \ ,\ L_i <i \\ i \ ,\ L_i =i \\end{aligned}\right.
​ 直接求和取模即可,时间复杂度O(m)

#include<bits/stdc++.h>
#define N 10000005
#define mod 1000000007
#define LL long long
using namespace std ;
int a[N],c[N],n,m ;
inline void read(int &num)
{
   int s = 0 ; char ch = getchar();
   while(ch < '0' || ch > '9') ch = getchar(); 
   while(ch >= '0' && ch <= '9') s = (s<<3) + (s<<1) + ch - '0', ch = getchar();
   num = s;
}//快读,可以用scanf代替
int main() {
    int n,m ;
    read(n),read(m) ;
    for(int i=1;i<=n;++i) read(a[i]) ;
    LL ans=0 ; int x ;
    while(m--)
    {
        read(x) ; LL res ;
        if(x==a[x]) res=x ;
        else res=1 ;
        ans=(ans+res)%mod ;
    }
    printf("%lld\n",ans) ;
    return 0 ;
}

Solution M(Raksasa的棋局):

本题可以预处理出馬踩的所有的位置最快需要多少步(bfs),将其存储在数组中,O(1)查询即可

#include <bits/stdc++.h>
using namespace std;
#define inf 0x7ffffffffffffff
#define N 1010
#define mod 1000000007
#define mod2 998244353
#define ll long long
ll dp[N][N];
ll dx[8]={1,2,2,1,-1,-2,-2,-1};
ll dy[8]={-2,-1,1,2,-2,-1,1,2};//馬的8个走向
int main()
{
    ll n,m,q;
    scanf("%lld%lld%lld",&n,&m,&q);
    ll x1,y1;
    scanf("%lld%lld",&x1,&y1);
    queue<pair<ll,ll>> qu;
    qu.push({x1,y1});
    while(!qu.empty())
    {
        pair<ll,ll> pa=qu.front();
        qu.pop();
        for(int i=0;i<8;i++)
        {
            ll x=pa.first+dx[i];
            ll y=pa.second+dy[i];
            if(x==x1&&y==y1) continue;
            if(x<1||x>n||y<1||y>m) continue;
            if(!dp[x][y])
            {
                dp[x][y]=dp[pa.first][pa.second]+1;
                qu.push({x,y});
            }
        }
    }
    while(q--)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        if(dp[x][y]) printf("%lld\n",dp[x][y]);
        else printf("-1\n");
    }
    return 0;
}

Solution N(Raksasa的数字):

把数组中每一个数字都转换成二进制,异或1会把数字“翻转”

例如: 1 异或 1为0,0异或 1 为 1。

统计每一位(转换成二进制)上有多少个1和0,因为异或1会把所有的1变成0,0变成1。所有就等同于1和0的数目交换。所有就算一下性价比,尽量往小的换即可。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x7ffffffffffffff
#define N 1005000
#define mod 1000000007
#define mod2 998244353
#define ll long long
vector<bitset<32>> ve;
ll cnt[32];
int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        ll n;
        ve.clear();
        scanf("%lld",&n);
        for(int i=0;i<n;i++)
        {
            ll x;
            scanf("%lld",&x);
            bitset<32> bi(x);
            ve.push_back(bi);
        }
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<32;j++)
            {
                if(ve[i][j]) cnt[j]++;
            }
        }
        ll ans=0;
        for(int i=0;i<32;i++)
        {
            if(cnt[i]>n/2)
            {
                ans+=(1ll<<i);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇