2020杭电多校第六场
T1001 Road To The 3rd Building
总的方案数有 n(n+1)/2 种。然后考虑每种方案的贡献:
设s[i]为前缀和,Ans是总的贡献,ans[i]是中间过程的累加和。
ans[i] = s[n-i+1] - s[i-1] + ans[i-1]
Ans= $\sum_{i=1}^n$ ans[i]/i
期望就是总的贡献除以总的方案数了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod= 1e9+7; 5 const int N=2e5+10; 6 ll s[N]; 7 ll ans[N]; 8 ll quick_pow(ll x,ll y){ 9 ll res=1; 10 while(y){ 11 if(y&1) res=res*x%mod; 12 x=x*x%mod; 13 y>>=1; 14 } 15 return res; 16 } 17 int main() 18 { 19 int T; 20 scanf("%d",&T); 21 while(T--){ 22 ll Ans=0; 23 ll n; 24 scanf("%lld",&n); 25 for(int i=1;i<=n;i++){ 26 scanf("%lld",&s[i]); 27 s[i]=(s[i]+s[i-1])%mod; 28 } 29 for(int i=1;i<=n;i++){ 30 ans[i]=(s[n-i+1]-s[i-1]+ans[i-1]+mod)%mod; 31 ll inv=quick_pow(i,mod-2); 32 Ans=(Ans+ans[i]*inv%mod)%mod; 33 } 34 ll inv=quick_pow(n*(n+1)%mod,mod-2); 35 Ans=(Ans*2%mod*inv)%mod; 36 printf("%lld\n",Ans); 37 } 38 return 0; 39 }
T1002 Little Rabbit's Equation
先找出方程最小可能是几进制(通过方程中的数字、字母判断),然后从 最小可能的进制 到16进制循环模拟,找到符合方程的最小进制。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 char s[30]; 5 int main() 6 { 7 while(~scanf("%s",s+1)){ 8 int len=strlen(s+1); 9 int mx=2; 10 for(int i=1;i<=len;i++){ 11 if(s[i]>='0'&&s[i]<='9') 12 mx=max(mx,s[i]-48+1); 13 if(s[i]>='A'&&s[i]<='F') 14 mx=max(mx,s[i]-'A'+10+1); 15 } 16 bool flag=0; 17 for(ll k=mx;k<=16;k++){ 18 ll x=0,y=0,z=0; 19 int i=1,j; 20 while(s[i]!='+'&&s[i]!='-'&&s[i]!='*'&&s[i]!='/'){ 21 if(s[i]>='A'){ 22 x=x*k+s[i]-'A'+10; 23 } 24 else x=x*k+s[i]-48; 25 i++; 26 } 27 if(s[i]=='+') j=1; 28 else if(s[i]=='-') j=2; 29 else if(s[i]=='*') j=3; 30 else j=4; 31 i++; 32 while(s[i]!='='){ 33 if(s[i]>='A'){ 34 y=y*k+s[i]-'A'+10; 35 } 36 else y=y*k+s[i]-48; 37 i++; 38 } 39 i++; 40 while(i<=len){ 41 if(s[i]>='A'){ 42 z=z*k+s[i]-'A'+10; 43 } 44 else z=z*k+s[i]-48; 45 i++; 46 } 47 //printf("%lld %lld %lld\n",x,y,z); 48 if(j==1&&x+y==z){ 49 flag=1; 50 printf("%lld\n",k); 51 break; 52 } 53 if(j==2&&x-y==z){ 54 flag=1; 55 printf("%lld\n",k); 56 break; 57 } 58 if(j==3&&x*y==z){ 59 flag=1; 60 printf("%lld\n",k); 61 break; 62 } 63 if(j==4&&x==y*z){ 64 flag=1; 65 printf("%lld\n",k); 66 break; 67 } 68 } 69 if(flag==0) printf("-1\n"); 70 } 71 return 0; 72 }
T1005 Fragrant numbers
区间dp,通过观察,只需要前13个数便可代表所有的数来构造前5000个数(当然,数不一定全部构造出来)。
dp[ i ][ j ][ k ] 表示 i - j 区间能否构造出k。
对于每个询问,只需要找到dp[ 1 ][ i ][ n ] = 1 的最小的右端点 i 。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=5010; 5 const int a[13]={1,1,4,5,1,4,1,9,1,9,1,1,4}; 6 bool dp[20][20][N]; 7 int ans[N]; 8 int getnum(int l,int r){ 9 int num=0; 10 for(int i=l;i<=r;i++) 11 num=num*10+a[i-1]; 12 return num; 13 } 14 void Prepare(){ 15 for(int i=1;i<=13;i++){ 16 dp[i][i][a[i-1]]=1; 17 } 18 for(int len=1;len<13;len++){ 19 for(int i=1;i+len<=13;i++){ 20 int j=i+len; 21 if(len<4){ 22 int x=getnum(i,j); 23 if(x<=5000) dp[i][j][x]=1; 24 } 25 for(int k=i;k<j;k++){ 26 for(int u=1;u<=5000;u++){ 27 if(dp[i][k][u]==0) continue; 28 for(int v=1;v<=5000;v++){ 29 if(dp[k+1][j][v]==0) continue; 30 if(u+v<=5000) dp[i][j][u+v]=1; 31 if(u*v<=5000) dp[i][j][u*v]=1; 32 if(u+v>5000&&u*v>5000) break; 33 } 34 } 35 } 36 } 37 } 38 memset(ans,-1,sizeof(ans)); 39 for(int i=13;i>=1;i--) 40 for(int j=1;j<=5000;j++) 41 if(dp[1][i][j]) 42 ans[j]=i; 43 } 44 int main() 45 { 46 Prepare(); 47 int T; 48 scanf("%d",&T); 49 while(T--){ 50 int n; 51 scanf("%d",&n); 52 printf("%d\n",ans[n]); 53 } 54 return 0; 55 }
T1006 A Very Easy Graph Problem
有一个明显的结论 $\sum_{i=1}^n$ $2^i$ < $2^{n+1}$
所以可以从第一条边开始建立最小生成树,因为根据以上结论,每两个点之间的最短路经过的边一定在最小生成树上。
这样可以统计最小生成树上每条边的贡献(经过的次数*边长)。我的方法是直接两次dfs,第一次统计子树中0和1的个数,第二次计算贡献。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod=1e9+7; 5 const int N=1e5+10; 6 const int M=2e5+10; 7 int head[N],to[M],nxt[M]; 8 ll w[M]; 9 int tot; 10 int n,m; 11 int a[N]; 12 int s[N]; 13 ll num0[N],num1[N]; 14 ll ans; 15 void init(){ 16 tot=0; 17 ans=0; 18 memset(head,0,sizeof(head)); 19 } 20 int find_set(int x){ 21 if(x!=s[x]) return s[x]=find_set(s[x]); 22 return s[x]; 23 } 24 inline void add(int u,int v,ll x){ 25 to[++tot]=v,nxt[tot]=head[u],w[tot]=x,head[u]=tot; 26 } 27 void dfs(int u,int fa){ 28 if(a[u]==0) num0[u]=1,num1[u]=0; 29 else num1[u]=1,num0[u]=0; 30 for(int i=head[u];i;i=nxt[i]){ 31 int v=to[i]; 32 if(v==fa) continue; 33 dfs(v,u); 34 num0[u]+=num0[v]; 35 num1[u]+=num1[v]; 36 } 37 } 38 void dfs2(int u,int fa){ 39 for(int i=head[u];i;i=nxt[i]){ 40 int v=to[i]; 41 if(v==fa) continue; 42 ans=(ans+(num0[v]*(num1[1]-num1[v]+mod)%mod+num1[v]*(num0[1]-num0[v]+mod)%mod)*w[i]%mod)%mod; 43 dfs2(v,u); 44 } 45 } 46 int main() 47 { 48 int T; 49 scanf("%d",&T); 50 while(T--){ 51 init(); 52 scanf("%d%d",&n,&m); 53 for(int i=1;i<=n;i++){ 54 scanf("%d",&a[i]); 55 s[i]=i; 56 } 57 ll val=1; 58 for(int i=1;i<=m;i++){ 59 int u,v; 60 scanf("%d%d",&u,&v); 61 val=val*2%mod; 62 int x=find_set(u),y=find_set(v); 63 if(x!=y){ 64 s[y]=x; 65 add(u,v,val); 66 add(v,u,val); 67 } 68 } 69 dfs(1,0); 70 dfs2(1,0); 71 printf("%lld\n",ans); 72 } 73 return 0; 74 }
T1009 Divisibility
在b进制下,一个数被x整除的充要条件是所有位上的数加起来可以被x整除,则(b-1)是x的倍数。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod= 998244353; 5 6 int main() 7 { 8 int T; 9 scanf("%d",&T); 10 while(T--){ 11 ll b,x; 12 scanf("%lld%lld",&b,&x); 13 if((b-1)%x==0){ 14 printf("T\n"); 15 } 16 else printf("F\n"); 17 } 18 return 0; 19 }
T1010 Expectation
先用矩阵树定理求出所有生成树的个数。
然后进行二进制拆位。即对于边权的每一位,找出边权的那一位为1的所有边,再次用矩阵树定理求出该位为1的所有边的生成树的个数,乘上该位的值,从而算出每一位的贡献。
把每一位的贡献加起来除以生成树的总个数,便是期望。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=110; 5 const int M=1e4+10; 6 const int mod=998244353; 7 struct edge{ 8 int from,to; 9 ll d; 10 }e[M]; 11 ll a[N][N]; 12 ll ans; 13 int n,m; 14 ll quick_pow(ll x,ll y){ 15 ll res=1; 16 while(y){ 17 if(y&1) res=res*x%mod; 18 x=x*x%mod; 19 y>>=1; 20 } 21 return res; 22 } 23 void work(){ 24 for(int i=2;i<=n;i++){ 25 if(!a[i][i]) 26 for(int j=i+1;j<=n;j++){ 27 if(a[j][i]){ 28 ans=-ans; 29 swap(a[i],a[j]); 30 break; 31 } 32 } 33 ll inv=quick_pow(a[i][i],mod-2); 34 for(int j=i+1;j<=n;j++){ 35 ll tmp=a[j][i]*inv%mod; 36 for(int k=i;k<=n;k++){ 37 a[j][k]=(a[j][k]-a[i][k]*tmp%mod+mod)%mod; 38 } 39 } 40 } 41 } 42 void solve(ll lim){ 43 ans=1; 44 for(int i=1;i<=n;i++) 45 for(int j=1;j<=n;j++) 46 a[i][j]=0; 47 for(int i=1;i<=m;i++){ 48 int u=e[i].from,v=e[i].to; 49 ll w=e[i].d; 50 if(!(w&lim)) continue; 51 a[u][u]++; 52 a[v][v]++; 53 a[u][v]--; 54 a[v][u]--; 55 } 56 work(); 57 for(int i=2;i<=n;i++) 58 ans=(ans*a[i][i])%mod; 59 ans=(ans%mod+mod)%mod; 60 } 61 int main() 62 { 63 int T; 64 scanf("%d",&T); 65 while(T--){ 66 scanf("%d%d",&n,&m); 67 for(int i=1;i<=m;i++){ 68 scanf("%d%d%lld",&e[i].from,&e[i].to,&e[i].d); 69 } 70 ll x=0; 71 for(int i=0;i<30;i++){ 72 ll lim=(1<<i); 73 solve(lim); 74 x=(x+lim*ans%mod)%mod; 75 } 76 solve((1<<30)-1); 77 ll sum=ans; 78 ll inv=quick_pow(sum,mod-2); 79 printf("%lld\n",x*inv%mod); 80 } 81 return 0; 82 }