2020 省选模拟测试 Round #9 solution (20/02/08)

【比赛链接】http://59.61.75.5:8018/contest/219

A. 树

【题意】

在一棵有根树上,任意两点间的最短路径都能够分为两个阶段:

- 从起点出发,沿着向根的方向走若干条边。

- 向着终点,沿着离开根的方向走若干条边。

定义一条路径的权值为向上走的边数乘向下走的边数。特殊地,当起点等于终点的时候,两阶段的边数都是 $0$;当起点是终点的祖先的时候,第一阶段的边数是 $0$;当终点是起点的祖先的时候,第二阶段的边数是 $0$ :这三种情况下,路径的权值都是 $0$。

给定一棵 $n$ 个节点的无根树 $T$ 和 $m$ 条路径。对于每一个 $r\in [1,n]$,你需要计算当 $r$ 为根节点时,所有路径的权值和。

【数据范围】$n,m\le 3\times 10^5$。

【题解】

手算一遍样例,就大概清楚题目的做法了。显然每条路径在一段上贡献的答案是一个等差数列,在三个节点处打上标记,差分即可。细节详见代码。

效率 $O(n \log n)$,瓶颈在于求 $lca$。期望得分:100。

【代码】

 1 #include<bits/stdc++.h>
 2 const int maxn=300000+10;
 3 int dep[maxn],Log[maxn],p[maxn][22],n;
 4 long long ans[maxn],res[maxn],Ans;
 5 std::vector<int> e[maxn];
 6 inline int lca ( int u,int v )
 7 {
 8     if ( dep[u]<dep[v] ) std::swap(u,v);
 9     for ( int i=Log[dep[u]];~i;i-- ) if ( dep[p[u][i]]>=dep[v] ) u=p[u][i];
10     if ( u==v ) return u;
11     for ( int i=Log[dep[u]];~i;i-- ) if ( p[u][i]!=p[v][i] ) u=p[u][i],v=p[v][i];
12     return p[u][0];
13 }
14 inline void dfs ( int u,int fr )
15 {
16     dep[u]=dep[p[u][0]=fr]+1;
17     for ( int i=1;p[u][i-1];i++ ) p[u][i]=p[p[u][i-1]][i-1];
18     for ( int v:e[u] ) if ( v!=fr ) dfs(v,u);
19 }
20 inline void solve ( int u,int fr )
21 {
22     for ( int v:e[u] ) if ( v!=fr ) solve(v,u),ans[u]+=ans[v],res[u]+=res[v];
23 }
24 inline void calc ( int u,int fr )
25 {
26     ans[u]=ans[u]*dep[u]+res[u]+ans[fr];
27     for ( int v:e[u] ) if ( v!=fr ) calc(v,u);
28 }
29 signed main()
30 {
31     int m;scanf("%d%d",&n,&m);
32     for ( int i=2,u,v;i<=n;i++ ) scanf("%d%d",&u,&v),e[u].push_back(v),e[v].push_back(u),Log[i]=Log[i>>1]+1;
33     dfs(1,0);
34     for ( int u,v,l;m--; )
35         scanf("%d%d",&u,&v),l=lca(u,v),
36         ans[u]-=2,res[u]+=2*dep[l]-dep[v]+dep[u]+1,
37         ans[v]-=2,res[v]+=2*dep[l]-dep[u]+dep[v]+1,
38         ans[l]+=4,res[l]-=4*dep[l]+2,
39         Ans+=1LL*(dep[u]-dep[l])*(dep[v]-dep[l]);
40     solve(1,0);calc(1,0);
41     for ( int i=1;i<=n;i++ ) printf("%lld\n",ans[i]+Ans);
42     return 0;
43 }
DTOJ4709

B. 方程

【题意】

方程:$$x_1\oplus x_2\oplus \cdots \oplus x_n =k$$

其中 $\oplus$ 指按位异或。

同时,$\forall i\in[1,n], 0\le x_i\le m_i$.

求解的个数。多组数据。

【数据范围】$T \leq 100,n\le 50,0\le k,m_i<2^{31}$。

【题解】

同样手算一遍样例。发现显然可以逐位贪心,若首位不为0则显然后面的位置可以随意选来补齐。

因此考虑逐位 $dp$。设 $f[i][j]$ 表示前 $i$ 个最高位为 $1$ 的数选了 $j$ 个的方案数。转移方程参见代码。

效率 $O(T \times 31n^2)$。期望得分:100。

【代码】

 1 #include<bits/stdc++.h>
 2 const int mod=1000000007;
 3 int n,k,a[100],f[100][100],ans;
 4 inline int power ( int x,int y )
 5 {
 6     int z=1;
 7     for ( ;y;y>>=1,x=1LL*x*x%mod ) if ( y&1 ) z=1LL*z*x%mod;
 8     return z;
 9 }
10 signed main()
11 {
12     while ( ~scanf("%d%d",&n,&k) )
13     {
14         int m=0;
15         for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]),m^=a[i];
16         ans=(m==k);
17         for ( int i=30;~i;i-- ) if ( (m>>(i+1))==(k>>(i+1)) )
18         {
19             for ( int j=0;j<=n;j++ ) for ( int l=0;l<=n;l++ ) f[j][l]=0;
20             f[0][0]=1;
21             for ( int j=1;j<=n;j++ )
22             {
23                 for ( int l=0;l<=n;l++ ) f[j][l]=(f[j][l]+1LL*(f[j-1][l]*(1+a[j]%(1<<i))))%mod;
24                 if ( (a[j]>>i)&1 ) for ( int l=0;l<=n;l++ ) f[j][l+1]=(f[j][l+1]+f[j-1][l])%mod;
25             }
26             for ( int j=1;j<=n;j++ ) if ( ((j^(m>>i)^(k>>i))&1)^1 ) ans=(ans+1LL*power((1<<i)%mod,j-1)*f[n][j])%mod;
27         }
28         printf("%d\n",ans);
29     }
30     return 0;
31 }
DTOJ4713

C. 圈

【题意】

对一张无重边、无自环的 $n$ 个点的无向图,定义圈为可以重复经过同一个点多次、但不能多次经过同一条边的环。例如,$1\to 2\to 1$ 不是一个合法的圈,而 $1\to 2\to 3\to 4\to 5\to 3\to 1$ 是一个合法的圈。

定义无向图的双圈覆盖为:图的若干个圈,使得图中每条边恰好出现在两个圈中(无论方向)。

一个定理:如果一个图有哈密尔顿回路的话,它就一定有双圈覆盖。

现在给定一张无重边、无自环的 $n$ 个点的无向图,保证这个图存在哈密尔顿回路(会给出),且每个点的度数至少为 $3$。

求它的一个双圈覆盖。

【数据范围】$T\le 100,4\le n\le 50,n\le m\le 500$。

【题解】

考虑题目的限制:度数大于等于 $3$。

这个性质不好用,考虑改造,使度数等于 $3$。

先把哈密顿回路扔出来,作为第一个圈。

若一个点 $x$ 有不在哈密顿回路上的边有 $(x,y),(x,z)$,我们新建⽴立一个点 $p$,假设 $x$ 在哈密顿回路上下一个点是 $w$,则把 $(x,w)$ 拆成 $(x,p),(p,w)$,$(x,z)$ 变成 $(p,z)$。我们对这个图求完圈覆盖后,只要把 $x,p$ 变回同一个点,且缩掉 $(x,p)$ 即可。至此转化完毕。

此时只剩下偶数个点。将哈密顿回路上的边分为两类,分别匹配一遍即可。分法很多,可采用如下办法:所有匹配边 $+\{(2i,2i+1)\}$ 与所有匹配边 $+\{(2i,2i-1)\}$。

考虑具体实现,将边黑白染色即可,而不用写出拆边的代码。

【代码】

 1 #include<bits/stdc++.h>
 2 inline int read ( void )
 3 {
 4     int x=0;char ch;
 5     while ( !isdigit(ch=getchar()) ) ;
 6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
 7     return x;
 8 }
 9 struct edge { int v,x; } G[1000][1000];
10 std::vector<std::vector<int>> Circles;
11 std::vector<int> Cir;
12 int n,m,siz[1000],map[1000][1000];
13 bool vis[1000][1000];
14 inline edge Next ( int u,int x )
15 {
16     if ( x!=siz[u] ) return (edge){u,x+1};
17     if ( u==n ) return (edge){1,1};
18     return (edge){u+1,1};
19 }
20 inline edge Prev ( int u,int x )
21 {
22     if ( x!=1 ) return (edge){u,x-1};
23     if ( u==1 ) return (edge){n,siz[n]};
24     return (edge){u-1,siz[u-1]};
25 }
26 inline void Ins ( int u )
27 {
28     if ( (int)Cir.size() and Cir[(int)Cir.size()-1]==u ) return;
29     Cir.push_back(u);
30 }
31 inline void dfs ( int u,int x,int k )
32 {
33     vis[u][x]=true;Ins(u);bool f=(map[u][x]==k);
34     edge w=f?Next(u,x):Prev(u,x);Ins(w.v);
35     while ( w.x==1 ) w=(f?Next(w.v,w.x):Prev(w.v,w.x)),Ins(w.v);
36     u=w.v;x=w.x;Ins(u);vis[u][x]=true;w=G[u][x];u=w.v;x=w.x;
37     if ( !vis[u][x] ) Ins(u),vis[u][x]=true,dfs(u,x,k);
38 }
39 signed main()
40 {
41     for ( int T=read();T--; )
42     {
43         Circles.clear();n=read();m=read();
44         for ( int i=1;i<=n;i++ ) for ( int j=1;j<=m;j++ ) G[i][j]=(edge){0,0};
45         for ( int i=1;i<=n;i++ ) siz[i]=1;
46         for ( int i=1;i<=m;i++ )
47         {
48             int u=read(),v=read();
49             if ( u>v ) std::swap(u,v);
50             if ( v==u+1 ) continue;
51             if ( u==1 and v==n ) continue;
52             ++siz[u];++siz[v];
53             G[u][siz[u]]=(edge){v,siz[v]};
54             G[v][siz[v]]=(edge){u,siz[u]};
55         }
56         for ( int i=1;i<=n;i++ ) Cir.push_back(i);
57         Circles.push_back(Cir);Cir.clear();
58         int k=0;
59         for ( int i=1;i<=n;i++ ) for ( int j=2;j<=siz[i];j++ ) k^=1,map[i][j]=k;
60         for ( int i=1;i<=n;i++ ) for ( int j=1;j<=siz[i];j++ ) vis[i][j]=false;
61         for ( int i=1;i<=n;i++ ) for ( int j=2;j<=siz[i];j++ ) if ( !vis[i][j] ) dfs(i,j,1),Circles.push_back(Cir),Cir.clear();
62         for ( int i=1;i<=n;i++ ) for ( int j=1;j<=siz[i];j++ ) vis[i][j]=false;
63         for ( int i=1;i<=n;i++ ) for ( int j=2;j<=siz[i];j++ ) if ( !vis[i][j] ) dfs(i,j,0),Circles.push_back(Cir),Cir.clear();
64         printf("%d\n",(int)Circles.size());
65         for ( auto Circle:Circles )
66         {
67             printf("%d",(int)Circle.size());
68             for ( int x:Circle ) printf(" %d",x);
69             puts("");
70         }
71     }
72     return 0;
73 }
DTOJ4714

D. 期望逆序对

【题意】

有 $n$ 个独立的随机变量,其中 $x_i$ 的值是一个从 $[l_i,r_i]$ 中随机选取的整数。

你可以将这 $n$ 个随机变量重新排列,从而得到一个长度为 $n$ 的随机变量序列。

求由随机变量序列生成的序列的逆序对个数的期望的最小值。

【数据范围】$1\le n\le 5000,\, 1\le l_i,r_i\le 10^9$。

【题解】

显然按中点排序即可。证明自行理解。

统计直接枚举两个区间算贡献,记得预处理长度逆元保证复杂度。

其实也可以用线段树或树状数组维护每个值的期望个数,可以减小复杂度。

效率 $O(n^2)$ 或 $O(n \log n)$。期望得分:100。

【代码】

 1 #include<bits/stdc++.h>
 2 const int mod=998244353,inv2=(mod+1)>>1;
 3 struct Pair { int l,r; } p[5010];
 4 int n,inv[5010],ans,len[5010];
 5 inline int power ( int x,int y )
 6 {
 7     int z=1;
 8     for ( ;y;y>>=1,x=1LL*x*x%mod ) if ( y&1 ) z=1LL*z*x%mod;
 9     return z;
10 }
11 signed main()
12 {
13     scanf("%d",&n);
14     for ( int i=1;i<=n;i++ ) scanf("%d%d",&p[i].l,&p[i].r);
15     std::sort(p+1,p+n+1,[&](const Pair &p1,const Pair &p2){return p1.l+p1.r<p2.l+p2.r;});
16     for ( int i=1;i<=n;i++ ) len[i]=p[i].r-p[i].l+1,inv[i]=power(p[i].r-p[i].l+1,mod-2);
17     for ( int i=1;i<n;i++ ) for ( int j=i+1;j<=n;j++ )
18     {
19         if ( p[i].l<=p[j].l and p[j].r<=p[i].r )
20             ans=(ans+1LL*len[j]*(len[j]-1)/2%mod*inv[i]%mod*inv[j]%mod+1LL*(p[i].r-p[j].r)*len[j]%mod*inv[i]%mod*inv[j]%mod)%mod;
21         else if ( p[j].l<=p[i].l and p[i].r<=p[j].r )
22             ans=(ans+1LL*len[i]*(len[i]-1)/2%mod*inv[i]%mod*inv[j]%mod+1LL*(p[i].l-p[j].l)*len[i]%mod*inv[i]%mod*inv[j]%mod)%mod;
23         else if ( p[i].l<=p[j].l and p[j].l<=p[i].r )
24             ans=(ans+1LL*(p[i].r-p[j].l+1)*(p[i].r-p[j].l)/2%mod*inv[i]%mod*inv[j]%mod)%mod;
25     }
26     return !printf("%d\n",ans);
27 }
DTOJ4715
posted @ 2020-02-08 20:19  DTOI_RSY  阅读(207)  评论(0编辑  收藏  举报