20191023

前言

  • 改题最快的一次考试……
  • 好久没看到过这么友好的题了,出题人++rp。

T1

  • 小Q的分数只和最高位的1有关。
  • 直接找就行了,找到就是小Q赢,没找到就是平局。
  • 代码巨短。
  • 时间复杂度$\Theta(N)$,空间复杂度$\Theta(1)$。
#include<cstdio>
int main(){
    int T,n,c,x,y;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n),c=0;
        for(register int i=1;i<=n;++i)scanf("%d",&x),c^=x;
        for(register int i=1;i<n;++i)scanf("%d%d",&x,&y);
        puts(c?"Q":"D");
    }
    return 0;
}
View Code

T2

  • $\Theta(N^2)$暴力非常显然,直接枚举中间位置即可。
  • 至于优化……
  • 需要一个非常基础的柿子:$C_n^i=C_n^{n-i}$。
  • 这个柿子很简单,难在想到用它去转化思路。
  • 原本的柿子大约是$\sum\limits_{i=0}^n C_n^i+C_m^{i+1}$之类的东西。
  • 然后你把$C_n^i$看成$C_n^{n-i}$,这样n-i和i+1的和就固定了。
  • 又因为$i\in[0,n]$,所以这个柿子可以理解成$C_{n+m}^{n+1}$,就可以直接计算了。
  • 时空复杂度$\Theta(N)$。
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
int const N=2e5+5,mod=1e9+7;
int n;
char s[N];
int a[N],b[N];
ll fac[N],inv[N],ans;
inline int read(){
    int ss(0);char bb(getchar());
    while(bb<48||bb>57)bb=getchar();
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss;
}
inline ll power(ll x,int y){
    ll as=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)as=as*x%mod;
    return as;
}
inline ll C(int x,int y){
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
inline int min(int x,int y){
    return x<y?x:y;
}
int main(){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    scanf("%s",s+1);
    n=strlen(s+1);
    for(register int i=1;i<=n;++i)a[i]=a[i-1]+(s[i]=='(');
    for(register int i=n;i;--i)b[i]=b[i+1]+(s[i]==')');
    fac[0]=fac[1]=1;
    for(register int i=2;i<=n;++i)fac[i]=fac[i-1]*i%mod;
    inv[n]=power(fac[n],mod-2);
    for(register int i=n;i;--i)inv[i-1]=inv[i]*i%mod;
    for(register int i=1;i<=n;++i)
        if(s[i]=='(')
            ans=(ans+C(a[i]+b[i]-1,a[i]))%mod;
    printf("%lld",ans);
    return 0;
}
View Code

T3

  • $\Theta(N^3K)$的算法应该不难想到。
  • 应该不是Floyd,至少我是先枚举的起点再枚举的中间点。可以直接想出来,不用借助什么算法理解。
  • 正解是考虑分块,算出经过$100\times k(k\in [0,100])$条边的最短距离,再算出至少经过0~100条边的最短距离。
  • 这样10000以内的询问都可以拆成$100\times a+b$的形式($a\in[0,100],b\in[0,100)$)。
  • 每次只需$\Theta(N)$枚举中间点即可。
  • 设A=100,时间复杂度$\Theta(AN^3+QN)$,空间复杂度$\Theta(AN^2)$,可以通过此题。
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
int const N=51,lar=0x3f3f3f3f;
int n,m;
int dis[152][N][N],f[102][N][N];
int a[N][N];
int head[N],to[N*N],Next[N*N],w[N*N],t;
inline int read(){
    int ss(0),pp(1);char bb(getchar());
    for(;bb<48||bb>57;bb=getchar())if(bb=='-')pp=-1;
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss*pp;
}
inline void add(int x,int y,int z){
    to[++t]=y,w[t]=z;
    Next[t]=head[x],head[x]=t;
    return ;
}
inline void _min(int &x,int y){
    x<y?x:(x=y);
    return ;
}
signed main(){
    n=read(),m=read();
    memset(dis,0x3f,sizeof(dis));
    memset(f,0x3f,sizeof(f));
    memset(a,0x3f,sizeof(a));
    while(m--){
        int ff=read(),tt=read(),ww=read();
        if(ww<a[ff][tt])a[ff][tt]=ww;
    }
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=n;++j)
            if(a[i][j]!=lar)add(i,j,a[i][j]);
    for(register int i=1;i<=n;++i)dis[0][i][i]=0;
    for(register int s=1;s<=150;++s)
        for(register int i=1;i<=n;++i)
            for(register int j=1;j<=n;++j)
                for(register int k=head[j],now=dis[s-1][i][j];k;k=Next[k])
                    _min(dis[s][i][to[k]],now+w[k]);
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=n;++j)
            f[0][i][i]=0,f[1][i][j]=dis[100][i][j];
    for(register int s=2;s<=100;++s)
        for(register int i=1;i<=n;++i)
            for(register int j=1;j<=n;++j)
                for(register int k=head[j],now=f[s-1][i][j];k;k=Next[k])
                    _min(f[s][i][to[k]],now+dis[100][j][to[k]]);
    for(register int s=150;~s;--s)
        for(register int i=1;i<=n;++i)
            for(register int j=1;j<=n;++j)
                _min(dis[s][i][j],dis[s+1][i][j]);
    m=read();
    while(m--){
        int x=read(),y=read(),z=read(),ans=lar;
        for(register int i=1;i<=n;++i)
            _min(ans,f[z/100][x][i]+dis[z%100][i][y]);
        printf("%lld\n",ans>=lar?-1ll:ans);
    }
    return 0;
}
View Code
posted @ 2019-10-25 07:07  remarkable  阅读(182)  评论(1编辑  收藏  举报