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的题!
- JZWC【Day2】题解&总结
- JZWC【Day1】题解&总结
- JZWC【Day3】题解&总结
- JZWC【Day4】题解&总结
- NOIP2011提高组 DAY2 题解&总结
- NOIP2014 Day2 模拟赛赛后总结&题解
- 【20150905】NOIP模拟套题01 day2 题解 & 总结
- 【20150915】NOIP模拟套题02 day2 题解 & 总结
- noip2011题解day2
- AOI2009 DAY2 题解
- 11-Day2 题解
- noip2011(day2)题解
- JSOI2015 Round2 Day2题解
- 【NOIP2013 day2】华容道 题解
- 集训题解-Day2
- [题解]NOIP2017 Day2 Solution
- NOIP 2017 Day2 题解?
- NOIP2011.day2.观光公交 题解
- ARM板安装Qt
- Numpy之random模块
- Reverse Integer
- [Mysql for Excel指南] 4 在Excel中编辑MySQL数据
- Android 开发中常用 ADB 命令总结
- JZWC【Day2】题解&总结
- 运行page页面时的事件执行顺序
- C++使用priority_queue方法
- 【PA2015】【BZOJ4292】Równanie
- Elmah 日志记录组件
- 2016年1月23日 后台生成HTML table 传到前台页面
- RedHat Enterprise Linux6.4 使用CentOS yum源
- AAA 扛鼎 传智播客 hadoop伪分布式安装笔记
- Ebean查找,条件仅针对目标表格join的表格的信息举例