考试的时候用哈希水过了第一题原本想用哈希只能够得20左右没想到因为数据过于水A了html
而后雨天的尾巴骗了5分,总分105 我太菜了c++
首先时间分配的不合理:第一题大水题ac自动机打完了都不会,第二题略微想了想打了个高斯消元,而后样例没过......,最后输出了一个随机数,第三题(lca板子忘了,打错一个地方,没有调出来)最后骗了五分git
考后主要讲一下第二题:记忆的轮廓(bzoj4899)和第三题:雨天的尾(yi)巴(bzoj3307)数组
FJ把杂志上全部的文章摘抄了下来并把它变成了一个长度不超过的字符串S。他有一个包含n个单词的列表,列表里的n个单词记t1 ....tn为他但愿从S中删除这些单词。
FJ每次在S中找到最先出现的列表中的单词(最先出现指该单词的开始位置最小),而后从S中删除这个单词。他重复这个操做直到S中没有列表里的单词为止。注意删除一个单词后可能会致使S中出现另外一个列表中的单词
FJ注意到列表中的单词不会出现一个单词是另外一个单词子串的状况,这意味着每一个列表中的单词在S中出现的开始位置是互不相同的
请帮助FJ完成这些操做并输出最后的S串1...tNt_NtN。他但愿从S中删除这些单词。
FJ每次在S中找到最先出现的列表中的单词(最先出现指该单词的开始位置最小),而后从S中删除这个单词。他重复这个操做直到S中没有列表里的单词为止。注意删除一个单词后可能会致使S中出现另外一个列表中的单词
FJ注意到列表中的单词不会出现一个单词是另外一个单词子串的状况,这意味着每一个列表中的单词在S中出现的开始位置是互不相同的
请帮助FJ完成这些操做并输出最后的S数据结构
没什么好讲的,ac自动机而后开栈维护ide
这个题仍是挺有意思的code
题目中提到这一句话
““咱们能够把这个地形看作是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,在[1,n]中,一共有n个节点。咱们把编号在[1,n]的叫作正确节点,[n+1,m]的叫作错误节点。””
那么题目中就暗示了1--n的路径确定是一条链
首先题目中说简单路径
其次具体能够反证出来
好比假设有三个点1-2 1-3 各有一条边那么1-3的简单路径就只有两个节点与n(n==3)个节点矛盾因此1-n的路径必定是一条链
这一个小点必定要读出来
设d[i]为i的出度,g[i]为错误儿子i返回存档指望步数,s[i]为当前点走到全部错误节点g之和
设a[i][j]表示以i为最新存档点走到j时指望步数,f[i][j]为以i为下一个存档点当前还剩余j个存档点
咱们逆着转移f,f能够由任意一个在i以后的点而且剩余存档数量为j-1的f贡献
而后分析 这个dp实际上就是在1-n一条链上进行的, 那么咱们其实就能够写出一个”相似“于线性dp的方程式
$f[i][j]=min(f[i][j],f[k][j-1]+a[i][k])$
这里a数组求法分析
设c是从j-1走到j的指望步数
a[i][j]=a[i][j-1]+c
分析
首先1/d[j-1]几率走到正确节点
其余能够由首先走到错误节点son
而后返回存档i,再继续走一个a[i][j-1],而后再加上从j-1走到j的指望步数
这里有一个注意点 这里的c在Σ中要加一(走到错误节点要走一步)
因而
$c=1/d[j-1]+Σ(g[son]+a[i][j-1]+c+1)$// (son表示j-1的错误儿子)
分析咱们将全部g相加就是s
$c=(1/d[j-1])+(d[j-1]-1)/d[j-1]+(1/d[j-1])*s[j-1]+((d[j-1]-1)/d[j-1])*a[i][j-1]+((d[j-1]-1)/d[j-1])*c$
移项
$1/d[j-1]*c=1+1/d[j-1]*s[j-1]+((d[j-1]-1)/d[j-1])*a[i][j-1];$
首先要预处理出走到错误节点返回的指望,具体能够经过一个简单dfs处理
而后咱们再次相乘得出
$c=d[j-1]+s[j-1]+(d[j-1]-1)*a[i][j-1];$
最后相加
得出
$a[i][j]=a[i][j-1]*d[j-1]+d[j-1]+s[j-1];$
而后
$f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]);$
那么咱们推测对于a数组来讲 它的快速增加确定会爆
而后咱们记录一个step,推测每次转移大体最大相差40步左右(但我以为这么作是qj测试点)
而后就有了if(k-i>40) break;
通过实际测试(因为测试点过水) k-i 取到20左右就能够了
如下是本人丑陋的代码
#include<bits/stdc++.h> #define ll long long #define db double #define A 2500 using namespace std; ll t,tot=0,n,m,p,head[A],nxt[A],ver[A]; db g[A],a[A][A],cur,f[A][A],d[A],s[A]; bool flag[A]; inline void add(ll x,ll y) {nxt[++tot]=head[x];head[x]=tot;ver[tot]=y;} inline ll read() { ll x=0,f=1;char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } return f*x; } void dfs(ll x) { flag[x]=1; g[x]=1.0; for(ll i=head[x];i;i=nxt[i]) { ll y=ver[i]; if(!flag[y]) dfs(y); g[x]+=1.0/d[x]*g[y]; } } int main() { t=read(); while(t--) { tot=0; memset(flag,0,sizeof(flag)); memset(head,0,sizeof(head)); memset(nxt,0,sizeof(nxt)); memset(ver,0,sizeof(ver)); memset(d,0,sizeof(d)); memset(g,0,sizeof(g)); memset(flag,0,sizeof(flag)); memset(f,125,sizeof(f)); n=read();m=read();p=read(); for(ll i=1;i<=m-n;i++) { ll x,y; x=read(),y=read(); add(x,y);d[x]++; } for(ll i=1;i<n;i++) d[i]++; for(ll i=1;i<=n;i++) if(!flag[i])dfs(i); for(ll i=1;i<=n;i++) { s[i]=0; for(ll j=head[i];j;j=nxt[j]) { ll y=ver[j]; s[i]+=g[y]; } } for(ll i=1;i<=n;i++) { a[i][i]=0; for(ll j=i+1;j<=n;j++) a[i][j]=a[i][j-1]*d[j-1]+s[j-1]+d[j-1]; } f[n][1]=0.0; for(ll j=2;j<=p;j++) { for(ll i=1;i<=n;i++) for(ll k=i+1;k<=n;k++) { if(k-i>40) break; f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]); } } db ans=f[1][p]; printf("%.4lf\n",ans); } return 0; }
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define db double 4 #define A 2500 5 using namespace std; 6 ll t,tot=0,n,m,p,head[A],nxt[A],ver[A]; 7 db g[A],a[A][A],cur,f[A][A],d[A],s[A]; 8 bool flag[A]; 9 inline void add(ll x,ll y) 10 {nxt[++tot]=head[x];head[x]=tot;ver[tot]=y;} 11 inline ll read() 12 { 13 ll x=0,f=1;char c=getchar(); 14 while(!isdigit(c)) 15 { 16 if(c=='-') 17 f=-1; 18 c=getchar(); 19 } 20 while(isdigit(c)) 21 { 22 x=(x<<1)+(x<<3)+c-'0'; 23 c=getchar(); 24 } 25 return f*x; 26 } 27 void dfs(ll x) 28 { 29 flag[x]=1; 30 g[x]=1.0; 31 for(ll i=head[x];i;i=nxt[i]) 32 { 33 ll y=ver[i]; 34 if(!flag[y]) 35 dfs(y); 36 g[x]+=1.0/d[x]*g[y]; 37 } 38 } 39 int main() 40 { 41 t=read(); 42 while(t--) 43 { 44 tot=0; 45 memset(flag,0,sizeof(flag)); 46 memset(head,0,sizeof(head)); 47 memset(nxt,0,sizeof(nxt)); 48 memset(ver,0,sizeof(ver)); 49 memset(d,0,sizeof(d)); 50 memset(g,0,sizeof(g)); 51 memset(flag,0,sizeof(flag)); 52 memset(f,125,sizeof(f)); 53 n=read();m=read();p=read(); 54 for(ll i=1;i<=m-n;i++) 55 { 56 ll x,y; 57 x=read(),y=read(); 58 add(x,y);d[x]++; 59 } 60 for(ll i=1;i<n;i++) 61 d[i]++; 62 for(ll i=1;i<=n;i++) 63 if(!flag[i])dfs(i); 64 for(ll i=1;i<=n;i++) 65 { 66 s[i]=0; 67 for(ll j=head[i];j;j=nxt[j]) 68 { 69 ll y=ver[j]; 70 s[i]+=g[y]; 71 } 72 } 73 for(ll i=1;i<=n;i++) 74 { 75 a[i][i]=0; 76 for(ll j=i+1;j<=n;j++) 77 a[i][j]=a[i][j-1]*d[j-1]+s[j-1]+d[j-1]; 78 } 79 f[n][1]=0.0; 80 for(ll j=2;j<=p;j++) 81 for(ll i=1;i<=n;i++) 82 for(ll k=i+1;k<=n;k++) 83 { 84 if(k-i>40) break; 85 f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]); 86 } 87 db ans=f[1][p]; 88 printf("%.4lf\n",ans); 89 } 90 return
也是一道不错的题
思考咱们若是按照既定套路,进行树上拆分的话
咱们还须要维护每个节点出现的值中最大的是什么
咱们首先能够想到开一个v[n][max_size]数组
而后要i至j的z值+1就
v[i][z]++,v[j][z]++,v[lca][z]--,v[f[lca][0]][z]--;
若是只是简单开数组会MLE+TLE
为了省时间+省空间
咱们能够创建n棵权值线段树
若是咱们不举行别的措施仍然会MLE,故使用动态开点线段树
另外因为值特别大而咱们开的是权值线段树,咱们能够进行离散化也能够采起”别的操做“
别的操做:具体来讲例若有m组询问,咱们值域只须要开到m 其实仍是和离散化差很少
最后统一进行线段树合并
完了(数据结构题真不知道要说些什么)
如下依然是本人丑陋的代码
#include<bits/stdc++.h> #define A 100005 #define ll int using namespace std; bool flag[A*2]; ll cnt=0,Ans[A*60],ans[A*60],deep[A*2],f[A][24],head[A*2],next[A*2],ver[A*2],lc[A*60],rc[A*60],tot=0,T[A*60]; ll n,m,sum[A]; inline ll read() { ll f=1,x=0;char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } return f*x; } void pushup(ll now) { if(ans[lc[now]]>=ans[rc[now]]) ans[now]=ans[lc[now]],Ans[now]=Ans[lc[now]]; else ans[now]=ans[rc[now]],Ans[now]=Ans[rc[now]]; } void add(ll x,ll y) { next[++tot]=head[x]; head[x]=tot; ver[tot]=y; } ll merge(ll x,ll y,ll l,ll r) { if(l==r&&x&&y) ans[x]+=ans[y]; if(!x||!y) return y+x; ll mid=(l+r)>>1; lc[x]=merge(lc[x],lc[y],l,mid); rc[x]=merge(rc[x],rc[y],mid+1,r); if(l!=r)pushup(x); return x; } void change(ll &p,ll l,ll r,ll pos,ll v) { if(!p) p=++cnt; if(l==r) {ans[p]+=v;Ans[p]=l;return ;} ll mid=(l+r)>>1; if(pos<=mid) change(lc[p],l,mid,pos,v); else change(rc[p],mid+1,r,pos,v); if(l!=r) pushup(p); } void dfs(ll x,ll dep) { flag[x]=1,deep[x]=dep; for(ll i=head[x];i;i=next[i]) { ll y=ver[i]; if(flag[y]) continue; f[y][0]=x; deep[y]=dep; dfs(y,dep+1); } } ll lca(ll x,ll y) { if(deep[x]>deep[y]) swap(x,y); for(ll i=20;i>=0;i--) { if(deep[x]<=deep[f[y][i]]) y=f[y][i]; if(deep[x]==deep[y]) break; } if(x==y) return x; for(ll i=20;i>=0;i--) { if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; } return f[x][0]; } void dfs_(ll x) { flag[x]=1; for(ll i=head[x];i;i=next[i]) { ll y=ver[i]; if(flag[y]) continue; dfs_(y); T[x]=merge(T[x],T[y],1,A); } if(ans[T[x]]>0) sum[x]=Ans[T[x]]; } int main() { n=read(),m=read(); for(ll i=1;i<n;i++) { ll xx=read(),yy=read(); add(xx,yy); add(yy,xx); } dfs(1,1); f[1][0]=0; for(ll i=1;i<=20;i++) for(ll j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1]; ll LCA; for(ll i=1;i<=m;i++) { ll x=read(),y=read(),z=read(); LCA=lca(x,y); change(T[x],1,A,z,1); change(T[y],1,A,z,1); change(T[LCA],1,A,z,-1); change(T[f[LCA][0]],1,A,z,-1); } memset(flag,0,sizeof(flag)); dfs_(1); for(ll i=1;i<=n;i++) printf("%d\n",sum[i]); }