2018.8.6 Noip2018模擬測試賽(十九)
阿新 • • 發佈:2018-08-06
() strong 多少 sin 持久 省選 lin main noip
日期: |
八月六號 |
總分: |
300分 |
難度: |
提高 ~ 省選 |
得分: |
10分(MMP) |
題目目錄:
T1:Tree
T2:異或運算
T3:Tree Restoring
賽後反思:
Emmmmmmm……
一直在打第一題…… 結果考完才發現dp少了一種情況……
除此之外,我無話可說…… Emmmmmm……
題解:
T1:Tree
樹形背包dp,設$f[i][j][k(0/1/2)]$為$i$的子樹中,選$j$條邊,0:從$i$出發,到$i$結束/1:從$i$出發,到$i$的某個後代結束/2:後代開始,經過$i$,後代結束:
狀態轉移:
1 f[x][j+k+1][0]=min(f[x][j+k+1][0],f[x][j][0]+f[y][k][0]+2*e[i].dis); 2 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][0]+f[y][k][1]+e[i].dis); 3 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][1]+f[y][k][0]+2*e[i].dis); 4 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][1]+f[y][k][1]+e[i].dis); 5 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][0]+f[y][k][2]+2*e[i].dis); 6 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][2]+f[y][k][0]+2*e[i].dis);
這樣就能樹形dp,時間復雜度 $O(n^2)$
COMPLETE CODE:
1 #include<iostream> 2#include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 int n,k,x,y,z,tot=0,h[3005],siz[3005]; 7 long long f[3005][3005][3],ans=1e18; 8 struct Edge{ 9 int x,y,dis,next; 10 }e[6005]; 11 bool vis[3005]; 12 13 inline void add_edge(int x,int y,int z){ 14 e[++tot].x=y,e[tot].dis=z; 15 e[tot].next=h[x],h[x]=tot; 16 } 17 18 void dfs(int x,int fa){ 19 siz[x]=1; 20 f[x][0][0]=0; 21 f[x][0][1]=0; 22 for(int i=h[x];i;i=e[i].next){ 23 if(e[i].x==fa)continue; 24 dfs(e[i].x,x); 25 int y=e[i].x; 26 for(int j=siz[x]-1;j>=0;j--) 27 for(int k=siz[y]-1;k>=0;k--){ 28 f[x][j+k+1][0]=min(f[x][j+k+1][0],f[x][j][0]+f[y][k][0]+2*e[i].dis); 29 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][0]+f[y][k][1]+e[i].dis); 30 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][1]+f[y][k][0]+2*e[i].dis); 31 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][1]+f[y][k][1]+e[i].dis); 32 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][0]+f[y][k][2]+2*e[i].dis); 33 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][2]+f[y][k][0]+2*e[i].dis); 34 } 35 siz[x]+=siz[e[i].x]; 36 } 37 } 38 39 int main(){ 40 memset(f,0x7f,sizeof(f)); 41 scanf("%d%d",&n,&k); 42 for(int i=1;i<n;i++){ 43 scanf("%d%d%d",&x,&y,&z); 44 add_edge(x,y,z); 45 add_edge(y,x,z); 46 } 47 dfs(1,-1); 48 for(int i=1;i<=n;i++) 49 ans=min(ans,min(f[i][k-1][1],f[i][k-1][2])); 50 printf("%lld",ans); 51 }
T2:異或運算
可持久化Trie樹,我之前還不知道可持久化,現在才知道,這跟主席樹是一樣的,是通過差分,維護區間。
我們按位貪心,由於$n$很小,對於每一位枚舉$a$序列,統計現在的位置往優了走會有多少個值,如果大於$k$,可以走,如果小於$k$,所有反著走。
問題就這麽迎刃而解了。(貌似這題用動態開點的主席樹維護值域也能做……)
CODE:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 int n,m,q,x,y,l,r,k,cnt=0,tot=0; 6 int a[1005],b[300005],root[300005]; 7 struct Trie{ 8 int siz,next[2]; 9 }v[20000005]; 10 struct Node{ 11 int l,r,val; 12 }s[300005]; 13 14 void insert(int &o,int last,int x){ 15 o=++tot; 16 int p=o,q=last; 17 for(int i=31;i>=0;i--){ 18 v[p]=v[q],v[p].siz++; 19 v[p].next[(x>>i)&1]=++tot; 20 p=v[p].next[(x>>i)&1]; 21 q=v[q].next[(x>>i)&1]; 22 } 23 v[p].siz++; 24 } 25 26 int find(int cnt,int k){ 27 int ans=0; 28 for(int i=31;i>=0;i--){ 29 int sum=0; 30 for(int j=1;j<=cnt;j++){ 31 bool c=(s[j].val>>i)&1; 32 sum+=v[v[s[j].r].next[c^1]].siz 33 -v[v[s[j].l].next[c^1]].siz; 34 } 35 if(sum>=k){ 36 ans=ans<<1|1; 37 for(int j=1;j<=cnt;j++){ 38 bool c=(s[j].val>>i)&1; 39 s[j].r=v[s[j].r].next[c^1]; 40 s[j].l=v[s[j].l].next[c^1]; 41 } 42 }else{ 43 k-=sum,ans=ans<<1; 44 for(int j=1;j<=cnt;j++){ 45 bool c=(s[j].val>>i)&1; 46 s[j].r=v[s[j].r].next[c]; 47 s[j].l=v[s[j].l].next[c]; 48 } 49 } 50 } 51 return ans; 52 } 53 54 int main(){ 55 scanf("%d%d",&n,&m); 56 for(int i=1;i<=n;i++)scanf("%d",a+i); 57 for(int i=1;i<=m;i++)scanf("%d",b+i); 58 for(int i=1;i<=m;i++) 59 insert(root[i],root[i-1],b[i]); 60 scanf("%d",&q); 61 for(int i=1;i<=q;i++){ 62 scanf("%d%d%d%d%d",&x,&y,&l,&r,&k); 63 cnt=0; 64 for(int j=x;j<=y;j++) 65 s[++cnt]=(Node){root[l-1],root[r],a[j]}; 66 printf("%d\n",find(cnt,k)); 67 } 68 }
T3:Tree Restoring
很容易發現離所有點最遠的點定是樹的直徑的兩個端點。(我怎麽就沒想到呢?)
所有點的距離只能在$\left\lceil{d\over 2}\right\rceil$到$d$之間,同時直徑上的點兩兩對稱,判斷這些即可。
CODE:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 6 int n,x,a,b,t[105]; 7 8 int main(){ 9 scanf("%d",&n); 10 for(int i=1;i<=n;i++){ 11 scanf("%d",&x); 12 t[x]++,a=max(a,x); 13 } 14 b=a+1>>1; 15 for(int i=b+1;i<=a;i++) 16 if(t[i]<2){ 17 printf("Impossible"); 18 return 0; 19 } 20 if(a&1){ 21 if(t[b]<2){ 22 printf("Impossible"); 23 return 0; 24 }t[b]-=2; 25 }else{ 26 if(t[b]<1){ 27 printf("Impossible"); 28 return 0; 29 }t[b]-=1; 30 } 31 for(int i=1;i<=b;i++) 32 if(t[i]){ 33 printf("Impossible"); 34 return 0; 35 } 36 printf("Possible"); 37 }
2018.8.6 Noip2018模擬測試賽(十九)