7.27题解

T1

什么原根,矩阵乘,循环矩阵,还有什么倍增优化DP各种乱搞,说实话我没几个会的,打个暴力DP吧,还一直RE0,快自闭了,如果有缘,等我过掉他再填坑吧

T2

考思维和手推式子的好题啊,看的就是谁能发现一些别人发现不了的式子,当然了,我一个也没发现,建议自己拿张纸写一写,纯看别人题解真的看不懂

40分

emm,我考场上想出来的暴力,对于t=0倍增LCA求两点之间的距离,直接带入求b,可过第一个点,t=1最简单的办法就是高斯消元可过2,3,4,前提是高斯消元记得打对了,卡精度啊!!!

50分

多的那10分是针对一条链的,要开始推式子咯

$t=0$时,我们换个思路,由之前计算点的贡献,改为计算边的贡献,以中间点3为例,3左侧的边对于$b[3]$的贡献是每条边左侧点的$a$值之和,其实还算好想,可以自己画个样例,列一下原题的式子,拆一下合一下就可以发现它很显然了,分一下左右两侧,设$qian[i]=a[1]+a[2]+...+a[i]$,$hou[i]=a[i]+a[i+1]+...+a[n]$,也就是一个前缀一个后缀,那我们就可以得到$b[i]=qian[1]+qian[2]+...+qian[i-1]+hou[i+1]+hou[i+2]+...+hou[n]$,分别对前缀求前缀和,后缀求后缀和就可以O(n)得到了$b$数组了

$t=1$时显然依旧用刚才的式子

$b[1]=hou[2]+hou[3]+...+hou[n]$

$b[2]=qian[1]+hou[3]+hou[4]+...+hou[n]$

$b[3]=qian[1]+qian[2]+hou[4]+hou[5]+...+hou[n]$

$b[4]=qian[1]+...+qian[3]+hou[5]+hou[6]+...+hou[n]$

有好多相同的项啊,手不痒吗?不想消项吗,动手吧

$b[2]-b[1]=qian[1]-hou[2]$  $b[3]-b[2]=qian[2]-hou[3]$

$b[4]-b[3]=qian[3]-hou[4]$  ......  $b[n]-b[n-1]=qian[n-1]-hou[n]$

$qian$,$hou$数组之间有什么关系呢?显然设$SUM=a[1]+a[2]+a[3]+...+a[n]$,那么就有

$qian[i-1]+hou[i]=SUM$,$qian[i-1]=SUM-hou[i]$,带入又得到了一串式子

$b[2]-b[1]=SUM-2*hou[2]$  $b[3]-b[4]=SUM-2*hou[3]$

$b[4]-b[3]=SUM-2*hou[4]$  ......  $b[n]-b[n-1]=SUM-2*hou[n]$

又因为我们有$b[1]=hou[2]+hou[3]+...+hou[n]$,有没有想到什么?把刚才的$n-1$个式子加起来,就可以凑出两个$b[1]$了啊,好东西,$n-1$个式子做和然后消项会得到$b[n]-b[1]=(n-1)SUM-2*b[1]$,再移一下项,就可以得到$SUM=\frac{b[1]+b[n]}{n-1}$,把$SUM$带回刚才的$n-1$个式子,可以求得出了$hou[1]$之外的完整$hou$数组,$hou[i]=\frac{SUM-b[i]+b[i-1]}{2}$,那把求后缀和倒过来就可以求得除了$a[1]$之外的$a$数组,那$a[1]$怎么办呢,我们回到$b[2]-b[1]=qian[1]-hou[2]$,现在式子中只剩一个未知量$qian[1]$,诶,$qian[1]$不就是$a[1]$嘛,这不就搞定了

虽然我们推了这么一大片只有十分,但它对正解思路的贡献还是不可估量的

100分

正解来咯

$t=0$时,我们依旧用最初的暴力,通过一遍$DFS$跑一下$LCA$,求出$b[1]$,其实$LCA$很没劲,点$i$对$b[1]$的贡献就是$a[i]*(deep[i]-1)$,不需要$LCA$,O(n)扫一下就好了,知道了$b[1]$的话,我们可以找一找他的儿子们的$b$值和他的之间有没有什么关系,如图

$b[1]=a[1]*0+a[2]*1+a[3]*1+a[4]*1+a[5]*2+a[6]*2+a[7]*2+a[8]*2$

$b[2]=a[1]*1+a[2]*0+a[3]*2+a[4]*2+a[5]*1+a[6]*1+a[7]*3+a[8]*3$

一一对应一下,发现了什么?2为根的子树中的点对2的贡献都少了一,其余点的贡献都多了一,当然多一少一都是对于有父子关系的点来说的,我们用$size[i]$代表以$i$为根的子树中的$a$值之和,在刚才的$DFS$中可以顺便求出,那么$b[2]=b[1]-size[2]+(size[1]-size[2])=b[1]+size[1]-2*size[2]$,我们推广到所有有父子关系的点$b[son]=b[fa]+size[1]-2*size[son]$这样的话再跑一遍$dfs$就可以求得所有的$b$了

$t=1$,想想有没有些和$b$有关的式子,当然啦,刚才写的那个不就是嘛,依旧移项,可以得到$b[son]-b[fa]=size[1]-2*size[son]$,设$c[i]=size[1]-2*size[i]$,我们就可以求得除了$c[1]$之外的整个$c$数组

现在我们继续思考,$b[1]=?$,我们想啊,一个深度为$deep$的点,肯定是有$deep-1$个父节点,那么他就被包含进了除以$1$为根之外的$deep-1$个子树中(自己作为根也是一个子树),而恰好一个点对于$b[1]$的贡献就是$a[i]*(deep[i]-1)$,所以$b[1]=size[2]+size[3]+size[4]+...+size[n]$,有没有觉得$c$数组和他有些联系?我们给除$c[1]$之外的整个$c$数组加和,$tot=(n-1)size[1]-2*(size[2]+size[3]+...+size[n])$,眼熟吧,$tot=(n-1)size[1]-2*b[1]$,那就可以得到$size[1]=\frac{tot+2*b[1]}{n-1}$,那么同时你就会发现其他点的$size$也变得可求了$size[i]=\frac{size[1]-c[i]}{2}$,那再跑一遍$dfs$把用$a$求$size$的式子倒过来,就可以求得所有的$a$了,当然$a[1]$这次就不特殊了,一起就求出来了

这道题怎么说呢,思维量稍大,考思维的同时,部分分还考了板子,比较全面了

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #define int long long
 6 #define maxn 100100
 7 #define maxnnn 1100
 8 #define maxk 19
 9 using namespace std;
10 int t,fl,n,js;
11 int yz[maxn],qiu[maxn];
12 int head[maxn],to[maxn*2],xia[maxn*2];
13 int fa[maxn],deep[maxn],size[maxn],c[maxn];
14 void clear()
15 {
16     js=0;
17     memset(yz,0,sizeof(yz));  memset(qiu,0,sizeof(qiu));
18     memset(head,0,sizeof(head));  memset(to,0,sizeof(to));
19     memset(xia,0,sizeof(xia));  memset(fa,0,sizeof(fa));
20     memset(deep,0,sizeof(deep));  memset(size,0,sizeof(size));
21     memset(c,0,sizeof(c));
22 }
23 void add(int x,int y)
24 {
25     to[++js]=y;  xia[js]=head[x];  head[x]=js;
26 }
27 void dfs(int x)
28 {
29     size[x]=yz[x];
30     for(int i=head[x];i;i=xia[i])
31     {
32         int ls=to[i];
33         if(deep[ls]==0)
34         {
35             deep[ls]=deep[x]+1;  fa[ls]=x;
36             dfs(ls);  size[x]+=size[ls];
37         }
38     }
39 }
40 void Dfs(int x)
41 {
42     for(int i=head[x];i;i=xia[i])
43     {
44         int ls=to[i];
45         if(fa[ls]==x)  {qiu[ls]=qiu[x]-2*size[ls]+size[1];  Dfs(ls);}
46     }
47 }
48 void DFs(int x)
49 {
50     for(int i=head[x];i;i=xia[i])
51     {
52         int ls=to[i];
53         if(fa[ls]==0)  {fa[ls]=x;  c[ls]=yz[ls]-yz[x];  DFs(ls);}
54     }
55 }
56 void DFS(int x)
57 {
58     for(int i=head[x];i;i=xia[i])
59     {
60         int ls=to[i];
61         if(fa[ls]==x)  {size[x]-=size[ls];  DFS(ls);}
62     }
63 }
64 signed main()
65 {
66     scanf("%lld",&t);
67     while(t--)
68     {
69         clear();  scanf("%lld",&n);
70         for(int i=1;i<n;++i)
71         {
72             int u,v;  scanf("%lld%lld",&u,&v);
73             add(u,v);  add(v,u);
74         }
75         scanf("%lld",&fl);
76         if(fl==0)
77         {
78             for(int i=1;i<=n;++i)  scanf("%lld",&yz[i]);
79             deep[1]=1;  dfs(1);
80             for(int i=2;i<=n;++i)  qiu[1]+=yz[i]*(deep[i]-deep[1]);
81             Dfs(1);
82             for(int i=1;i<=n;++i)  printf("%lld ",qiu[i]);
83             puts("");
84         }
85         else
86         {
87             for(int i=1;i<=n;++i)  scanf("%lld",&yz[i]);
88             fa[1]=-1;  DFs(1);
89             int sum=0;
90             for(int i=2;i<=n;++i)  sum+=c[i];
91             size[1]=(sum+2*yz[1])/(n-1);
92             for(int i=2;i<=n;++i)  size[i]=(size[1]-c[i])/2;
93             DFS(1);
94             for(int i=1;i<=n;++i)  {qiu[i]=size[i];  printf("%lld ",qiu[i]);}
95             puts("");
96         }
97     }
98     return 0;
99 }
全都是dfs

 

T3

相比之下这道纯数学题反而不太难了

$opt=0$和上次的$T2visit$一毛一样,式子直接抄,而且这次还友好的不需要$CRT$了,直接求阶乘和逆元就可以

$opt=1$这是个卡特兰数,我看出来了,很开心,因为只在一条轴的一半上走,向右看作入栈,向左看作出栈,就是卡特兰数的基础模型

$opt=2$考试推了半个小时也没推出式子来,且忘了可以先打表找规律之类的,最后暴力没打完,死了,考后题解没看懂,所以选择了$DP$解决,由于空间不允许,所以我选择了把一个三维数组,拆成了两个二维数组和一个一维数组,不过可能换个题就不可用了

$opt=3$这次是组合数+卡特兰,我们假设选$i$步在竖直方向上走,那首先就会出现一个$C_n^i$,然后由于有不超过原点的限制,所以还是进出栈模型,由于算是有两个方向,所以是两个卡特兰数相乘,最终结果为$C_n^i*cat(\frac{i}{2})*cat(\frac{n-i}{2})$,看见$\frac{}{2}$没,记得保证偶数啊

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define ll long long
 5 #define maxn 100100
 6 #define maxx 1100
 7 using namespace std;
 8 const long long mod=1e9+7;
 9 int n,typ;
10 ll ans;
11 ll jc[maxn*2],ny[maxn*2],as[maxx];
12 ll ss[maxx][maxx*2],sp[maxx][maxx*2];
13 ll ksm(ll a,ll b,ll c)
14 {
15     ll ans=1;  a=a%c;
16     while(b)
17     {
18         if(b&1)  ans=(ans*a)%c;
19         b=b>>1;  a=(a*a)%c;
20     }
21     return ans%c;
22 }
23 void work1()
24 {
25     for(int u=0;u<=n/2;++u)
26     {
27         int l=n/2-u;
28         ans=(ans+(((((jc[n]*ny[u])%mod*ny[u])%mod)*ny[l]%mod)*ny[l])%mod)%mod;
29     }
30     printf("%lld\n",ans%mod);
31 }
32 void work2()
33 {
34     n=n/2;
35     ans=(((((jc[2*n]*ny[n])%mod*ny[n])%mod*jc[n])%mod)*ny[n+1])%mod;
36     printf("%lld\n",ans%mod);
37 }
38 void work3()
39 {
40     as[0]=1;
41     for(int o=1;o<=n;++o)
42     {
43         for(int i=0;i<=2*n;++i)
44         {
45             if(i==n)
46             {
47                 as[o]=(as[o]+ss[o-1][n-1])%mod;  as[o]=(as[o]+ss[o-1][n+1])%mod;
48                 as[o]=(as[o]+sp[o-1][n-1])%mod;  as[o]=(as[o]+ss[o-1][n+1])%mod;
49             }
50             else
51             {
52                 if(i-1!=n)
53                 {
54                     if(i-1>=0)
55                     {
56                         ss[o][i]=(ss[o][i]+ss[o-1][i-1])%mod;
57                         sp[o][i]=(sp[o][i]+sp[o-1][i-1])%mod;
58                     }
59                 }
60                 else  {ss[o][i]=(ss[o][i]+as[o-1])%mod;  sp[o][i]=(sp[o][i]+as[o-1])%mod;}
61                 if(i+1!=n)
62                 {
63                     ss[o][i]=(ss[o][i]+ss[o-1][i+1])%mod;
64                     sp[o][i]=(sp[o][i]+sp[o-1][i+1])%mod;
65                 }
66                 else  {ss[o][i]=(ss[o][i]+as[o-1])%mod;  sp[o][i]=(sp[o][i]+as[o-1])%mod;}
67             }
68         }
69     }
70     printf("%lld\n",as[n]%mod);
71 }
72 void work4()
73 {
74     for(int i=0;i<=n;i+=2)
75     {
76         ll ls1=((jc[n]*ny[i])%mod*ny[n-i])%mod;
77         ll ls2=((((jc[i]*ny[i/2])%mod*ny[i/2])%mod*jc[i/2])%mod*ny[i/2+1])%mod;
78         ll ls3=((((jc[n-i]*ny[(n-i)/2])%mod*ny[(n-i)/2])%mod*jc[(n-i)/2])%mod*ny[(n-i)/2+1])%mod;
79         ans=(ans+(((ls1*ls2)%mod)*ls3)%mod)%mod;
80     }
81     printf("%lld\n",ans%mod);
82 }
83 int main()
84 {
85     scanf("%d%d",&n,&typ);  jc[0]=1;
86     for(int i=1;i<=2*n;++i)  jc[i]=(jc[i-1]*i)%mod;
87     ny[2*n]=ksm(jc[2*n],mod-2,mod);
88     for(int i=2*n;i>=1;--i)  ny[i-1]=(ny[i]*i)%mod;
89     if(typ==0)  work1();
90     else if(typ==1)  work2();
91     else if(typ==2)  work3();
92     else work4();
93     return 0;
94 }
数学+DP

 

posted @ 2019-07-28 10:49  hzoi_X&R  阅读(154)  评论(0编辑  收藏  举报