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
",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
",x,y,z);
48             if(j==1&&x+y==z){
49                 flag=1;
50                 printf("%lld
",k);
51                 break;
52             }
53             if(j==2&&x-y==z){
54                 flag=1;
55                 printf("%lld
",k);
56                 break;
57             }
58             if(j==3&&x*y==z){
59                 flag=1;
60                 printf("%lld
",k);
61                 break;
62             }
63             if(j==4&&x==y*z){
64                 flag=1;
65                 printf("%lld
",k);
66                 break;
67             }
68         }
69         if(flag==0) printf("-1
");
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
",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
",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
");
15         }
16         else printf("F
");
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
",x*inv%mod);
80     }
81     return 0;
82 }
原文地址:https://www.cnblogs.com/--HY--/p/13491403.html