考試的時候用哈希水過了第一題原本想用哈希只能夠得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]); }