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; }
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; }
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; }
rp++