第一次用LaTeX
T1
判断树上两节点间的父子关系
这道题恰好是我以前给自己出的题,没想到这次居然真的碰上了
大部分人都用LCA来做,但是我有更好的方法$O(n)$
DFS的时候标记每个节点的DFS序$dfn[i]$,再标记该节点的子树中所有点的DFS序最大值$sub[i]$
如果$dfn[y]<dfn[x]\leq sub[y]$,那么说明x在y的子树中
1 #include <cstdio> 2 struct E{ 3 int to,next; 4 }e[2*40001]; 5 int x,y,n,m,sz=0,head[40001],dfn[40001],sub[40001],root,dnow=1; 6 void insert(int x,int y) 7 { 8 sz++; 9 e[sz].next=head[x]; 10 head[x]=sz; 11 e[sz].to=y; 12 } 13 int dfs(int x,int f) 14 { 15 dfn[x]=dnow; 16 for (int i=head[x];i;i=e[i].next) 17 if (e[i].to!=f) dnow++,dfs(e[i].to,x); 18 return sub[x]=dnow; 19 } 20 int main() 21 { 22 scanf("%d",&n); 23 for (int i=1;i<=n;i++) 24 { 25 scanf("%d%d",&x,&y); 26 if (y==-1) 27 { 28 root=x; 29 continue; 30 } 31 insert(x,y); 32 insert(y,x); 33 } 34 dfs(root,root); 35 scanf("%d",&m); 36 for (int i=1;i<=m;i++) 37 { 38 scanf("%d%d",&x,&y); 39 if (dfn[x]<dfn[y]&&dfn[y]<=sub[x]) 40 { 41 printf("1\n"); 42 continue; 43 } 44 if (dfn[y]<dfn[x]&&dfn[x]<=sub[y]) 45 { 46 printf("2\n"); 47 continue; 48 } 49 printf("0\n"); 50 } 51 return 0; 52 }
T2
A,B两组各有n个人,他们的实力值分别为$a_i$、$b_j$,两组之间随机比赛,每次比赛实力值大的一方得$(a_i-b_j)^2$,求a组得分减b组得分的数学期望,保留一位小数
根据题意,答案的表达式大概长这个样子:
$$\frac{\sum_{i=1}^{n}\sum_{j=1}^{n}\begin{cases}(a_i-b_j)^2&{a_i\geq b_j}\\-(a_i-b_j)^2&{a_i<b_j}\end{cases}}{n}$$
只观察分子,变形得到
$$\sum_{i=1}^{n}\sum_{j=1}^{n}\begin{cases}(a_i^2-2a_ib_j+b_j^2)&{a_i\geq b_j}\\-(a_i^2-2a_ib_j+b_j^2)&{a_i<b_j}\end{cases}$$
注意公式里面的$a_i$,$b_j$能被提出来,三项可以分开来求,里面的循环可以直接预处理
由于比分的正负性,我们需要快速把A中第i个人的和B组的所有比赛分成$a_i<b_j$和$a_i\geq b_j$两部分,我们可以在B中按实力值$b_i$维护$b_i$和$b_i^2$的前缀和,这样就可以在$O(1)$的时间内完成这两部分比赛的得分统计了
遍历A中元素时只需要把$a_i\geq b_j$比赛的部分取正(用前缀和求),$a_i<b_j$比赛的部分取负(用总和减前缀和),再把结果求和再除以n就行了
最后记得分子要先*10再/n放在long long里,再在/10和%10的部分中间添上小数点
但是我忘记四舍五入了,丢了50分
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #define ll long long 5 using namespace std; 6 ll n,a[50001],b[50001],preb[50001],preb2[50001],precnt[50001],a2,bsum,b2sum,ans; 7 int main() 8 { 9 ios::sync_with_stdio(false); 10 cin>>n; 11 for (int i=1;i<=n;i++) cin>>a[i]; 12 for (int i=1;i<=n;i++) cin>>b[i]; 13 sort(a+1,a+n+1); 14 sort(b+1,b+n+1); 15 for (int i=1;i<=n;i++) 16 { 17 precnt[b[i]]++; 18 preb[b[i]]+=b[i]; 19 preb2[b[i]]+=b[i]*b[i]; 20 } 21 for (int i=1;i<=50000;i++) 22 { 23 precnt[i]+=precnt[i-1]; 24 preb[i]+=preb[i-1]; 25 preb2[i]+=preb2[i-1]; 26 } 27 bsum=preb[50000]; 28 b2sum=preb2[50000]; 29 for (int i=1;i<=n;i++) 30 { 31 ans+=(a[i]*a[i]*(2*precnt[a[i]]-n)-(ll)2*a[i]*(2*preb[a[i]]-bsum)+2*preb2[a[i]]-b2sum); 32 } 33 ans=ans*10/n; 34 cout<<ans/10<<"."<<ans%10; 35 }
T3
1. 它有2*n个数位,n是正整数(允许有前导0)。
2. 构成它的每个数字都在给定的数字集合S中。
3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等
求合法的方案数mod 999983
好像可以先用DP求出来每种数字之和所对应的方案数,再用容斥原理求两种情况的并集,不过没时间做了
今天看了一下题解,和上面我写的的思路大概一样,不过求两种情况交集的地方还是需要认真考虑
先用DP求出$f[i][j]$表示i个数的和为j的方案数,因为每种情况都可以把长度为2*n的序列拆成两个长度为n的子序列分开统计方案数,再利用乘法原理求出组合成长度为2*n的序列的总方案数为$\sum_{i=0}^{maxs*n}f[n][i]^2$
为了求出两种情况的交集,我们需要具体分析一下满足什么条件的时候会重复统计,为了同时展开两种情况,我们可以先考虑2*a=8的情况
$$\begin{cases}a1+a2+a3+a4=a5+a6+a7+a8\\a1+a3+a5+a7=a2+a4+a6+a8\end{cases}$$
$$\begin{cases}a1+a3=a6+a8\\a2+a4=a5+a7\end{cases}$$
但是还要考虑奇数的情况
$$\begin{cases}a1+a2+a3=a4+a5+a6\\a1+a3+a5=a2+a4+a6\end{cases}$$
$$\begin{cases}a1+a3=a4+a6\\a2=a5\end{cases}$$
所以a1,a3,a4,a6和a2,a5又分别构成了两个第一种情况所述的序列,方案数为$\sum_{j=0}^{maxs*n/2}f[n/2][j]^2$和$\sum_{k=0}^{maxs*(n+1)/2}f[(n+1)/2][k]^2$,又因为两个序列相互独立,所以用乘法组合起来,得到交集的方案数是$\sum_{j=0}^{maxs*n/2}f[n/2][j]^2*\sum_{k=0}^{maxs*(n+1)/2}f[(n+1)/2][k]^2$
所以答案为两种方案的方案数和-两种方案的并集=$2*\sum_{i=0}^{maxs*n}f[n][i]^2-\sum_{j=0}^{maxs*n/2}f[n/2][j]^2*\sum_{k=0}^{maxs*(n+1)/2}f[(n+1)/2][k]^2$