JZWC【Day2】题解&总结

来源:互联网 发布:网络用语八组鹅 编辑:程序博客网 时间:2024/05/16 00:51

果然说Day1会水成狗,Day2被虐成狗,这次改题改的我都没有心情好好玩耍了……
附上题解

T1 送你一棵圣诞树

Description

给出m+1棵树,从0到m编号。T0只有一个编号为0的点。如果用Ti表示第i棵树(i>0),则在第Tai 棵树的第ci 个点和第Tbi 棵树的第di 个点之间连上了一条长度为li 的边来表示它。并且,保持Tai 中的所有节点编号不变,然后如果Tai 中有s 个节点,Tbi 中的所有节点的编号加上s。对于每棵树,求这里写图片描述 d(i,j)表示节点i到j的最短路径。

Input

第一行输入一个正整数T 表示数据组数。每组数据的第一行是一个整数m,接下来m 行每行五个整数ai, bi, ci, di, li,保证0 <= ai, bi < i, 0<= li<= 10^9,ci, di 存在。

Output

对于每组询问输出m 行。第i 行输出Ti 的权值。答案可能很大,请对10^9 + 7 取模后输出。

Sample Input

1
2
0 0 0 0 2
1 1 0 0 4

Sample Output

2
28

Data Constraint

对于30% 的数据,m <= 8
对于60% 的数据,m <= 16
对于100% 的数据,1 <= m<= 60,T<= 100

Solution

神题一道,改了两天。
首先,我们要知道,这样建出的树可能非常大,是不能用普通的方法表示的。
用ans[i]来表示Ti的答案,cout[i]表示Ti的节点个数,all(x,i)表示Tx中所有的点到i这个点的距离和,因为这棵树由两棵树组成,我们可以推出如下公式:
ans[i]=ans[a[i]]+ans[b[i]]+cout[a[i]] * cout[b[i]] * l[i]+all(a[i],c[i]) * cout[b[i]]+all(b[i],d[i])*cout[a[i]];
然后我们思考如何求all(x,i)。发现上面我们已经把一个问题分成了许多子问题,那么我们能不能再把all分开来求呢?
很明显是可以的。设to(x,i,j)表示Tx中,点i到点j的距离,则
若l为组成x的左子树,r为右子树,
(1)i存在于l中
all(x,i)=all(r,d[x])+all(l,i)+(l[x]+to(l,i,c[x]))*cout[r];
(2)i存在于r中
all(x,i)=all(l,c[x])+all(r,i)+(l[x]+to(r,i,d[x]))*cout[l];
有了上面的经验,我们也可以吧to(x,i,j)分开来求,详细见代码。
然后,我们可以写记忆化搜索,这样每一次to的复杂度是O(n)的,每一次all都要调用一次to,所以复杂度是O(n^2)。
总复杂度为O(n^3)

Code

#include<cstdio>#include<cstring>#include<map>#define fo(i,a,b) for(int i=a;i<=b;i++)#define ll long long#define mo 1000000007#define N 65using namespace std;map<pair<ll,ll>,ll> h[N];map<ll,ll> h1[N];struct note{    ll a,b,c,d,l,w;    void add() {h[w][make_pair(c,d)]=l;h[w][make_pair(d,c)]=l;}}f[N];ll cout[N],ans[N],c[N];ll ty,n;ll to(ll z,ll x,ll y) {    if (x==y) return 0;    if (h[z][make_pair(x,y)]) return h[z][make_pair(x,y)];    ll l=f[z].a,r=f[z].b,sum;    if (x<cout[l]&&y<cout[l]) sum=to(l,x,y);    else if (x<cout[l]&&y>=cout[l])     sum=(to(l,x,f[z].c)+f[z].l+to(r,y-cout[l],f[z].d))%mo;    else if (x>=cout[l]&&y<cout[l])     sum=(to(r,x-cout[l],f[z].d)+f[z].l+to(l,y,f[z].c))%mo;    else sum=to(r,x-cout[l],y-cout[l]);    h[z][make_pair(x,y)]=sum;    return sum;}ll all(ll x,ll y) {    if (!x) return 0;    if (h1[x][y]) return h1[x][y];    ll l=f[x].a,r=f[x].b,z;    if (y<cout[l]) z=(all(r,f[x].d)+all(l,y)+    (f[x].l+to(l,f[x].c,y))*c[r]%mo)%mo;    else z=(all(l,f[x].c)+all(r,y-cout[l])+    (f[x].l+to(r,f[x].d,y-cout[l]))*c[l]%mo)%mo;    h1[x][y]=z;    return z;}int main() {    for(scanf("%d",&ty);ty;ty--) {        scanf("%d",&n);        memset(cout,0,sizeof(cout));cout[0]=1;c[0]=1;        memset(ans,0,sizeof(ans));fo(i,1,n) h[i].clear(),h1[i].clear();        fo(i,1,n) {            scanf("%lld%lld%lld%lld%lld",            &f[i].a,&f[i].b,&f[i].c,&f[i].d,&f[i].l);            f[i].w=i;f[i].l=f[i].l%mo;            f[i].d=f[i].d+cout[f[i].a];            f[i].add();f[i].d=f[i].d-cout[f[i].a];            c[i]=(c[f[i].a]+c[f[i].b])%mo;            cout[i]=cout[f[i].a]+cout[f[i].b];            ans[i]=(ans[f[i].a]+ans[f[i].b]+            c[f[i].a]%mo*c[f[i].b]%mo*f[i].l%mo            +all(f[i].a,f[i].c)*c[f[i].b]%mo+            all(f[i].b,f[i].d)*c[f[i].a]%mo)%mo;            printf("%lld\n",ans[i]);        }     }}

T2 我想大声告诉你

Description

有n个人,每一轮随机选择没有出局的人一个人出局,然后剩下的人受到一次攻击,每个人被攻击就有 p的概率淘汰,求当k=0..n-1时,每个人受到k次攻击然后出局的概率是多少,答案在模258280327 意义下。注意,出局不等于淘汰。

Input

第一行输入一个正整数T 表示数据组数。
对于每一组数据输入仅一行三个数n, x, y,表示在这组数据中有n 个人参赛,p = x/y。保证y 和258280327 互质。

Output

对于每组数据,输出一行n 个整数,表示对于k = 0到n - 1 的概率在模258280327 意义下的值。

Sample Input

2
3 40 100
9 32 1049

Sample Output

172186885 92980918 16529941
229582513 163885050 39458156 102374877 116777758 216371874 55544199 95860736 8136787

Data Constraint

对于60% 的数据,n <=100
对于100% 的数据,n <= 2* 10^3,1 <= T <= 5,0<= x < y <= 10^9

Solution

没什么好说的了,首先你要理解清楚题意,然后再看懂样例,然后你就大概会做了。
因为它是随机选人出局,我们把它变成按顺序选人出局,对于每个k,所有人出局的总概率是不变的。而题目描述的每个人出局的概率是相等的,所以按我们只需要做一次线性的dp,算出答案,然后取个平均数就行了。

Code

#include<cstdio>#include<cstring>#define fo(i,a,b) for(int i=a;i<=b;i++)#define ll long long#define N 2005#define mo 258280327using namespace std;int ty,n;ll x,y,f[N][N],ans,p[N];ll mi(ll x,ll y) {    if (y==0) return 1;    ll z=x;y--;    while (y) {        if (y%2==1) z=z*x%mo;        x=x*x%mo;y/=2;    }       return z;}int main() {    for(scanf("%d",&ty);ty;ty--) {        scanf("%d%lld%lld",&n,&x,&y);        memset(f,0,sizeof(f));f[1][0]=1;        ll ni=mi(y,mo-2);p[0]=1;        fo(i,1,n) p[i]=p[i-1]*(y-x)%mo*ni%mo;        fo(i,1,n-1)             fo(j,0,n-1)                 if (f[i,j]) {                    f[i+1][j]=(f[i+1][j]+f[i][j]*(1-p[j]+mo)%mo)%mo;                    f[i+1][j+1]=(f[i+1][j+1]+f[i][j]*p[j+1]%mo)%mo;                }         fo(i,0,n-1) {            ll ans=0;            fo(j,1,n) ans=(ans+f[j][i])%mo;            printf("%lld ",ans*mi(n,mo-2)%mo);        }           printf("\n");    }   }

T3 对你的爱深不见底

Description

定义一些字符串,s1=’a’,s2=’b’,si=si-1+si-2,给出n,m,求sn的前m个字符形成的字符串中,最长的相等前后缀的长度。如,ababa为3。

Input

第一行输入一个正整数T 表示数据组数。
对于每组数据,第一行是两个整数n;m。保证1<= m <=|sn|

Output

对于每组数据,输出一个整数表示答案。答案可能很大,你只需要输出模258280327 后的答案。

Sample Input

2
4 3
5 5

Sample Output

1
2

Data Constraint

对于30% 的数据,n <= 20
对于60% 的数据,n <= 60
对于100% 的数据,n <= 10^3,1 <= T <= 100

Solution

首先,你得打个表,然后找规律。然后你就会发现,设k为最小的sk使得|sk|>m+1,那么答案就是m-|sk-2|,上高精度就行了。证明自行脑补,意会即可。

Code

#include<cstdio>#include<cstring>#include<iostream>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)#define N 1005#define maxn 100000000#define mo 258280327 #define ll long long using namespace std;struct note{    int l,a[505];}f[N],m,one;note add(note x,note y) {    note z;memset(z.a,0,sizeof(z.a));z.l=max(x.l,y.l);    fo(i,1,z.l) {        z.a[i]+=x.a[i]+y.a[i];z.a[i+1]+=z.a[i]/maxn;z.a[i]%=maxn;       }       if (z.a[z.l+1]) z.l++;    return z;}note dec(note x,note y) {    note z;memset(z.a,0,sizeof(z.a));z.l=x.l;    fo(i,1,z.l) {        z.a[i]=x.a[i]-y.a[i];        if (z.a[i]<0) z.a[i]+=maxn,x.a[i+1]--;    }    if (!z.a[z.l]) z.l--;    return z;}bool big(note x,note y) {    if (x.l>y.l) return 1;    else if (x.l<y.l) return 0;    fd(i,x.l,1)         if (x.a[i]>y.a[i]) return 1;        else if (x.a[i]<y.a[i]) return 0;    return 0;}char s[N];int ty,n,l,r,mid,ten[9];ll ans;int main() {    f[1].l=f[1].a[1]=f[2].l=f[2].a[1]=one.l=one.a[1]=1;ten[1]=1;    fo(i,2,8) ten[i]=ten[i-1]*10;ten[0]=ten[8];    fo(i,3,1003) f[i]=add(f[i-1],f[i-2]);    for(scanf("%d",&ty);ty;ty--) {        scanf("%d",&r);l=0;r+=3;m.l=0;memset(m.a,0,sizeof(m.a));        scanf("%s",s+1);int k=0,len=strlen(s+1);        fd(i,len,1) {            k=k+(s[i]-'0')*ten[(len-i+1)%8];            if ((len-i+1)%8==0) m.a[++m.l]=k,k=0;        }        if (k) m.a[++m.l]=k;        note p=add(m,one);        while (l<r) {            mid=(l+r)/2;            if (big(f[mid],p)) r=mid;else l=mid+1;        }        l-=2;        m=dec(m,f[l]);ans=0;        fd(i,m.l,1) ans=(ans*maxn%mo+m.a[i])%mo;        printf("%lld\n",ans);    }}

果然跪了呀!整场比赛都被第二题的题意搞得不要不要的,只打了第三题的暴力。看来策略还是有问题,不要死磕一道题,尤其是那么KD的题!

0 0
原创粉丝点击