“科大国创杯”2023 年安徽省青少年信息学科普日活动 简要题解

老年退役选手感受单调队列力量。

初中组没有实现,如果有问题欢迎爆 d 我。已经补上代码了


小学组

T1 grade

直接累加即可。不需要按百分比算(也就是别 /100),那样可能会出现一些浮点数误差。

 

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}   int n,a[10005]; ll X;
int main(){
    freopen("grade.in","r",stdin);
    freopen("grade.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++) X+=read()*a[i];
    for(int i=1;i<=n;i++) X-=read()*a[i];
    if(X>0) puts("ke");
    if(!X) puts("same");
    if(X<0) puts("do");
    return 0;
}
View Code

 



T2 order

暴力枚举 $t$ 就可以了

 

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
int main(){
    freopen("order.in","r",stdin);
    freopen("order.out","w",stdout);
    int tc=read();
    while(tc--){
        int p=read(),n=read(),q=n,t=1;
        while(q!=1) q=1ll*q*n%p,t++;
        printf("%d\n",t);
    }
    return 0;
}
View Code

 



T3 string

答案即为 $cnt4+cnt5-cnt20$。注意到对于一个数,我们只关心其个位和十位就可以判定了,然后就是 $O(n)$。

 

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1000005;
inline int read(){
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}   int n; char s[N]; ll X,Y,Z;
int main(){
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    n=read(); scanf("%s",s+1);
    for(int i=1;i<=n;i++){
        if(s[i]=='0'||s[i]=='5')
            X+=i;
        if(s[i]=='0'||s[i]=='4'||s[i]=='8')
            Y++;
        if(i>1 && ((s[i-1]-'0')*10+(s[i]-'0'))%4==0 )
            Y+=i-1;
        if(s[i]=='0')
            Z++;
        if(i>1 && ((s[i-1]-'0')*10+(s[i]-'0'))%20==0 )
            Z+=i-1;
    }   printf("%lld\n",X+Y-Z);
    return 0;
}
View Code

 


T4 hex

并查集维护连通性即可。注意如果不建虚点的话需要特判 $n=1$。

 

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}   int n,fa[10005],a[105][105];
inline int id(int i,int j){ return (i-1)*n+j; }
inline int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); }
inline void merge(int x,int y){
    int fx=find(x),fy=find(y);
    fa[fy]=fx;
}
inline void solve(){
    n=read();
    if(n==1){
        int x=read();
        if(x==1) puts("ke");
        else puts("yet");
        return;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            a[i][j]=read();
    for(int i=1;i<=n*n;i++) fa[i]=i;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(j<n && a[i][j]==1 && a[i][j+1]==1)
                merge(id(i,j),id(i,j+1));
            if(i<n && a[i][j]==1 && a[i+1][j]==1)
                merge(id(i,j),id(i+1,j));
            if(i<n && j>1 && a[i][j]==1 && a[i+1][j-1]==1)
                merge(id(i,j),id(i+1,j-1));
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(find(id(1,i))==find(id(n,j))){
                puts("ke");
                return;
            }
    for(int i=1;i<=n*n;i++) fa[i]=i;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(j<n && a[i][j]==-1 && a[i][j+1]==-1)
                merge(id(i,j),id(i,j+1));
            if(i<n && a[i][j]==-1 && a[i+1][j]==-1)
                merge(id(i,j),id(i+1,j));
            if(i<n && j>1 && a[i][j]==-1 && a[i+1][j-1]==-1)
                merge(id(i,j),id(i+1,j-1));
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(find(id(i,1))==find(id(j,n))){
                puts("do");
                return;
            }
    puts("yet");
}
int main(){
    freopen("hex.in","r",stdin);
    freopen("hex.out","w",stdout);
    int tc=read();
    while(tc--) solve();
    return 0;
}
View Code

 



初中组

T1 score

和小学组一样,不做除法就可以了。

 

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}   int n,m,a[105];
struct A{
    int x,y;
    bool operator < (const A &a) const { return x==a.x?y<a.y:x>a.x; }
}b[105];
int main(){
    freopen("score.in","r",stdin);
    freopen("score.out","w",stdout);
    n=read(); m=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) a[j]=read();
        sort(a+1,a+m+1);
        b[i].y=i;
        for(int j=2;j<m;j++) b[i].x+=a[j];
    }   sort(b+1,b+n+1);
    for(int i=1;i<=n;i++) printf("%d%c",b[i].y,i==n?'\n':' ');
    return 0;
}
View Code

 



T2 count

排好序,枚举一个 $i$,然后枚举 $j$ 的时候 $k$ 尺取一下,时间复杂度 $O(n^2)$。

记得开 long long。

 

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}   int n,a[8005]; ll ans;
int main(){
    freopen("count.in","r",stdin);
       freopen("count.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
        for(int j=i+1,k=i+1;j<=n;j++){
            while(k<n && a[i]+a[j]>a[k+1]) k++;
            ans+=k-j;
        }
    printf("%lld\n",ans);
    return 0;
}
View Code

 


T3 walk

先找全局最短路。

如果询问边不在最短路上,直接输出全局最短路。

这样本质不同的询问个数就是 $O(n)$ 了。

然后可以预处理 $(1,1) \to (x,y)$ 和 $(x,y) \to (n,n)$ 的最短路 单次询问简单讨论一下 $O(n)$ 是容易的。

总时间复杂度 $O(n^2+\min(n,q)n)$,即 $O(n^2)$。

 

#include<bits/stdc++.h>
#define y1 wtakioi
using namespace std;
typedef long long ll;
const int N=5005;
const ll inff=1ll<<60;
int n,q,B,d[N<<1],a[N][N],b[N][N],x1,x2,y1,y2; ll f[N][N],g[N][N],ask[N<<1]; bool path[N][N];
unsigned long long seed;
inline void xorshift64(){
    seed^=seed<<13;
    seed^=seed>>7;
    seed^=seed<<17;
}
inline ll solve(int x,int y,int o){
//    cerr<<"Ask "<<x<<' '<<y<<' '<<o<<'\n';
    ll ans=inff;
    if(o){
        for(int i=1;i<=x;i++)
            ans=min(ans,f[i][y+1]+g[i][y+1]);
        for(int i=1;i<y;i++)
            ans=min(ans,f[x+1][i]+g[x+1][i]);
    }
    else{
        for(int i=1;i<x;i++)
            ans=min(ans,f[i][y+1]+g[i][y+1]);
        for(int i=1;i<=y;i++)
            ans=min(ans,f[x+1][i]+g[x+1][i]);
    }
    return ans;
}
int main(){
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin>>n>>q>>seed>>B;
    for(int i=1;i<=n;i++){
        for(int j=1;j<n;j++){
            xorshift64();
              a[i][j]=seed&((1<<B)-1);
        }
      }
    for(int i=1;i<n;i++){
        for(int j=1;j<=n;j++){
            xorshift64();
              b[i][j]=seed&((1<<B)-1);
        }
      }
      for(int i=1;i<=n;i++) f[i][0]=f[0][i]=inff; f[0][1]=0;
      for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++){
              ll f1=f[i][j-1]+a[i][j-1],f2=f[i-1][j]+b[i-1][j];
              if(f1<=f2) f[i][j]=f1,path[i][j]=0;
              else f[i][j]=f2,path[i][j]=1;
        }
    for(int x=n,y=n;x>1||y>1;){
        if(path[x][y]==0)
            y--,d[x+y]=y;
        else
            x--,d[x+y]=x+n;
    }
    for(int i=1;i<=n;i++) g[n+1][i]=g[i][n+1]=inff; g[n+1][n]=0;
    for(int i=n;i;i--)
          for(int j=n;j;j--)
              g[i][j]=min(g[i][j+1]+a[i][j],g[i+1][j]+b[i][j]);
    while(q--){
        cin>>x1>>y1>>x2>>y2;
        int op=x2-x1,z=op?x1+n:y1;
        if(d[x1+y1]!=z){
            cout<<f[n][n]<<'\n';
            continue;
        }
        if(!ask[x1+y1]) ask[x1+y1]=solve(x1,y1,op);
        cout<<ask[x1+y1]<<'\n';
    }
    return 0;
}
View Code

 


T4 stone

考虑贪心。如果在还没有合并的点中,最小值就在当前可以合并到的位置,那么我们一定会去合并它

进一步的,以最小值在左边为例:设最小值在位置 $x$,我们可以直接将 $x$ 和 $x+1$ 看作一堆,因为合并到 $x+1$ 时,一定会立刻合并 $x$。

更进一步的,合并完之后的这若干堆石子,其贡献可以类似地看作它们的平均值。

于是考虑贪心在两边维护好这样的栈,以左边为例,从左向右扫,如果当前栈顶的平均值大于其下方的元素,则将它们合并。

数据随机下,栈内元素是 $O(\log n)$ 的。然后就预处理出每个栈的形态,询问时暴力地不停合并左右两个栈中较小的栈顶即可。

时间复杂度 $O(n \log n)$。

存在不依赖随机性质的做法。

 

 

#include<bits/stdc++.h>
#define mp make_pair
#define pii pair<ll,int>
using namespace std;
typedef long long ll;
const int N=100005;
const int M=31;
inline int read(){
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
int n,L,R,a[N],ltop[N],rtop[N]; pii lstc[N][M],rstc[N][M]; ll c1[N],c2[N],sum[N],ans;
inline bool check(pii x,pii y){ return x.first*y.second>y.first*x.second; }
inline pii add(pii x,pii y){
    x.first+=y.first; x.second+=y.second;
    return x;
}
int main(){
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
    n=read(),L=read(),R=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++){
        ltop[i]=ltop[i-1];
        for(int j=1;j<=ltop[i];j++) lstc[i][j]=lstc[i-1][j];
        lstc[i][++ltop[i]]=mp(a[i],1);
        while(ltop[i]>1 && check(lstc[i][ltop[i]],lstc[i][ltop[i]-1]))
            lstc[i][ltop[i]-1]=add(lstc[i][ltop[i]-1],lstc[i][ltop[i]]),ltop[i]--;
    }
    for(int i=n;i;i--){
        rtop[i]=rtop[i+1];
        for(int j=1;j<=rtop[i];j++) rstc[i][j]=rstc[i+1][j];
        rstc[i][++rtop[i]]=mp(a[i],1);
        while(rtop[i]>1 && check(rstc[i][rtop[i]],rstc[i][rtop[i]-1]))
            rstc[i][rtop[i]-1]=add(rstc[i][rtop[i]-1],rstc[i][rtop[i]]),rtop[i]--;
    }
    for(int i=n;i;i--) c1[i]=c1[i+1]+1ll*a[i]*i;
    for(int i=1;i<=n;i++) c2[i]=c2[i-1]+1ll*a[i]*(n-i+1);
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
    for(int i=L;i<=R;i++){
        int tx=ltop[i-1],ty=rtop[i+1],px=i,py=i,len=n-1; ans=1ll*a[i]*(n-1);
        while(tx||ty){
            if(!ty||(tx&&check(rstc[i+1][ty],lstc[i-1][tx]))){
                int xr=px-1,xl=xr-lstc[i-1][tx].second+1;
                len-=lstc[i-1][tx].second;
//                cerr<<"Left "<<xl<<' '<<xr<<' '<<len<<'\n';
                ans+=(sum[xr]-sum[xl-1])*len+c1[xl]-c1[xr+1]-(sum[xr]-sum[xl-1])*(xl-1);
                px-=lstc[i-1][tx].second; tx--;
            }
            else{
                int xl=py+1,xr=xl+rstc[i+1][ty].second-1;
                len-=rstc[i+1][ty].second;
//                cerr<<"Right "<<xl<<' '<<xr<<' '<<len<<'\n';
                ans+=(sum[xr]-sum[xl-1])*len+c2[xr]-c2[xl-1]-(sum[xr]-sum[xl-1])*(n-xr);
                py+=rstc[i+1][ty].second; ty--;
            }
        }
        printf("%lld%c",ans,i==R?'\n':' ');
    }
    return 0;
}
View Code

 

 

 


鲜花

初中组 T2 放过 $O(n^2\log n)$,T3 边权随机并放过小常数 $O(n^2+qn)$,水老师实乃良心出题人!!!

posted @ 2023-04-09 16:47  铃兰星夜  阅读(1592)  评论(3编辑  收藏  举报