無法逃避的是自我,而無法挽回的是過去。

前言

還算可以,不過 T1 少 \(\bmod\) 了一下掛了 25pts,T2 沒看清題面掛了 27pts。

下回注意吧。。

T1 Hunter

解題思路

感覺正解不是很好想到,但是看題解就比較好看懂。。

1 號獵人死亡的輪數等於在 1 號之前死亡的獵人數 +1。

根據期望的 線性 性, 就等於每個獵人比 1 號獵人先死的概率和。

不難發現第 i 個獵人比 1 號獵人先死的概率是 \(\dfrac{W_{i}}{W_{1}+W_{i}}\)

上面的內容直接從官方題解摘過來了,實現沒有任何難度。。

說一下記憶化搜尋吧。。。

很明顯 對於 \(n\le 10\) 的點一般的搜尋就可以了。

對於 \(n\le 20\)的就需要記憶化了。

記憶化向下搜尋的時候最好不要傳一些概率那一類的東西,比較難把控。

對於下一層的概率最好在這一層就算好。。。

然後再特判一下 1 的情況就好了。

code

25pts DFS

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0,f=1;
  9. char ch=getchar();
  10. while(ch>'9'||ch<'0')
  11. {
  12. if(ch=='-') f=-1;
  13. ch=getchar();
  14. }
  15. while(ch>='0'&&ch<='9')
  16. {
  17. x=(x<<1)+(x<<3)+(ch^48);
  18. ch=getchar();
  19. }
  20. return x*f;
  21. }
  22. const int N=1e5+10,M=1e6+10,mod=998244353;
  23. int n,ans,inv[M],s[N],f[1<<21];
  24. int ksm(int x,int y)
  25. {
  26. int tmp=1;
  27. while(y)
  28. {
  29. if(y&1) tmp=tmp*x%mod;
  30. x=x*x%mod;
  31. y>>=1;
  32. }
  33. return tmp%mod;
  34. }
  35. void dfs(int sta,int round,int E)
  36. {
  37. if(!(sta&1)) return ;
  38. ans=(ans+f[sta]*s[1]%mod*round%mod*E%mod)%mod;
  39. for(int i=1;i<=n;i++)
  40. if((sta>>i-1)&1)
  41. dfs(sta^(1<<i-1),round+1,E*f[sta]%mod*s[i]%mod);
  42. }
  43. signed main()
  44. {
  45. n=read();
  46. for(int i=1;i<=n;i++)
  47. s[i]=read();
  48. for(int i=1;i<(1<<n);i++)
  49. {
  50. int sum=0;
  51. for(int j=1;j<=n;j++)
  52. if((i>>j-1)&1)
  53. sum=(sum+s[j])%mod;
  54. f[i]=ksm(sum,mod-2);
  55. }
  56. dfs((1<<n)-1,1,1);
  57. printf("%lld",ans);
  58. return 0;
  59. }

45pts 記憶化 DFS

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0,f=1;
  9. char ch=getchar();
  10. while(ch>'9'||ch<'0')
  11. {
  12. if(ch=='-') f=-1;
  13. ch=getchar();
  14. }
  15. while(ch>='0'&&ch<='9')
  16. {
  17. x=(x<<1)+(x<<3)+(ch^48);
  18. ch=getchar();
  19. }
  20. return x*f;
  21. }
  22. const int N=1e5+10,M=1e6+10,mod=998244353;
  23. int n,ans,inv[M],s[N],f[1<<21],dp[1<<21];
  24. int ksm(int x,int y)
  25. {
  26. int tmp=1;
  27. while(y)
  28. {
  29. if(y&1) tmp=tmp*x%mod;
  30. x=x*x%mod;
  31. y>>=1;
  32. }
  33. return tmp%mod;
  34. }
  35. int dfs(int sta,int round)
  36. {
  37. if(!(sta&1)) return 0;
  38. if(dp[sta]) return dp[sta];
  39. dp[sta]=(dp[sta]+f[sta]*s[1]%mod*round%mod)%mod;
  40. for(int i=1;i<=n;i++)
  41. if((sta>>i-1)&1)
  42. dp[sta]=(dp[sta]+dfs(sta^(1<<i-1),round+1)*f[sta]%mod*s[i]%mod)%mod;
  43. return dp[sta];
  44. }
  45. signed main()
  46. {
  47. n=read();
  48. for(int i=1;i<=n;i++)
  49. s[i]=read();
  50. for(int i=1;i<(1<<n);i++)
  51. {
  52. int sum=0;
  53. for(int j=1;j<=n;j++)
  54. if((i>>j-1)&1)
  55. sum=(sum+s[j])%mod;
  56. f[i]=ksm(sum%mod,mod-2);
  57. }
  58. printf("%lld",dfs((1<<n)-1,1));
  59. return 0;
  60. }

正解

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0,f=1;
  9. char ch=getchar();
  10. while(ch>'9'||ch<'0')
  11. {
  12. if(ch=='-') f=-1;
  13. ch=getchar();
  14. }
  15. while(ch>='0'&&ch<='9')
  16. {
  17. x=(x<<1)+(x<<3)+(ch^48);
  18. ch=getchar();
  19. }
  20. return x*f;
  21. }
  22. const int mod=998244353,N=1e5+10;
  23. int n,ans,s[N];
  24. int ksm(int x,int y)
  25. {
  26. int tmp=1;
  27. while(y)
  28. {
  29. if(y&1) tmp=tmp*x%mod;
  30. x=x*x%mod;
  31. y>>=1;
  32. }
  33. return tmp%mod;
  34. }
  35. signed main()
  36. {
  37. n=read();
  38. for(int i=1;i<=n;i++)
  39. s[i]=read();
  40. for(int i=2;i<=n;i++)
  41. ans=(ans+s[i]*ksm((s[i]+s[1]%mod),mod-2)%mod)%mod;
  42. printf("%lld",(ans+1)%mod);
  43. return 0;
  44. }

T2 Defence

解題思路

首先感謝出題人沒有寫太多一個節點有多個法術的測試點。

其次,抱怨一下題目描述不清楚。。。

非常像之前做過的玫瑰花精這道題。

主要維護 7 個值,(其實有一些沒有用的,但也無傷大雅)。

最左邊的 1 的座標以及對應的最左側的連續的 0 的長度。

同樣的東西,在右邊也維護一個類似的。

還有一箇中間的最大區間的長度以及左右端點。

也就是下圖所表示的。

剩下的比較難寫的就是 push_up 函數了,別的還好。

然後就是線段樹合併一系列的事情了。。。。

code

AC程式碼

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. #define ls tre[x].l
  6. #define rs tre[x].r
  7. using namespace std;
  8. inline int read()
  9. {
  10. int x=0,f=1;
  11. char ch=getchar();
  12. while(ch>'9'||ch<'0')
  13. {
  14. if(ch=='-') f=-1;
  15. ch=getchar();
  16. }
  17. while(ch>='0'&&ch<='9')
  18. {
  19. x=(x<<1)+(x<<3)+(ch^48);
  20. ch=getchar();
  21. }
  22. return x*f;
  23. }
  24. const int N=1e5+10;
  25. int n,m,q,ans[N],root[N],fa[N];
  26. int tot=1,head[N],nxt[N],ver[N];
  27. int all;
  28. vector<int > p[N];
  29. struct Segment_Tree
  30. {
  31. int l,r,lp,rp,lmx,rmx,llen,rlen,len;
  32. }tre[N*80];
  33. void push_up(int x,int l,int r)
  34. {
  35. if(!ls&&!rs) return ;
  36. if(ls)
  37. {
  38. tre[x].lp=tre[ls].lp;
  39. tre[x].llen=tre[ls].lp-l;
  40. }
  41. else
  42. {
  43. tre[x].lp=tre[rs].lp;
  44. tre[x].llen=tre[rs].lp-l;
  45. }
  46. if(rs)
  47. {
  48. tre[x].rp=tre[rs].rp;
  49. tre[x].rlen=r-tre[rs].rp;
  50. }
  51. else
  52. {
  53. tre[x].rp=tre[ls].rp;
  54. tre[x].rlen=r-tre[ls].rp;
  55. }
  56. int len1=tre[ls].len,len2=tre[rs].len,len3=0;
  57. if(ls&&rs) len3=tre[ls].rlen+tre[rs].llen;
  58. if(len1>=max(len2,len3))
  59. {
  60. tre[x].len=len1;
  61. tre[x].lmx=tre[ls].lmx;
  62. tre[x].rmx=tre[ls].rmx;
  63. }
  64. else if(len2>=max(len1,len3))
  65. {
  66. tre[x].len=len2;
  67. tre[x].lmx=tre[rs].lmx;
  68. tre[x].rmx=tre[rs].rmx;
  69. }
  70. else
  71. {
  72. tre[x].len=len3;
  73. tre[x].lmx=tre[ls].rp;
  74. tre[x].rmx=tre[rs].lp;
  75. }
  76. }
  77. void insert(int &x,int l,int r,int pos)
  78. {
  79. if(!x) x=++all;
  80. if(l==r)
  81. {
  82. tre[x].lp=tre[x].rp=tre[x].lmx=tre[x].rmx=l;
  83. return ;
  84. }
  85. int mid=(l+r)>>1;
  86. if(pos<=mid) insert(ls,l,mid,pos);
  87. else insert(rs,mid+1,r,pos);
  88. push_up(x,l,r);
  89. }
  90. int merge(int x,int y,int l,int r)
  91. {
  92. if(!x||!y) return x+y;
  93. if(l==r) return x;
  94. int mid=(l+r)>>1;
  95. ls=merge(ls,tre[y].l,l,mid);
  96. rs=merge(rs,tre[y].r,mid+1,r);
  97. push_up(x,l,r);
  98. return x;
  99. }
  100. void add(int x,int y)
  101. {
  102. ver[++tot]=y;
  103. nxt[tot]=head[x];
  104. head[x]=tot;
  105. }
  106. void dfs(int x)
  107. {
  108. if(p[x].size())
  109. for(int i=0;i<p[x].size();i++)
  110. insert(root[x],1,m,p[x][i]);
  111. for(int i=head[x];i;i=nxt[i])
  112. {
  113. int to=ver[i];
  114. dfs(to);
  115. root[x]=merge(root[x],root[to],1,m);
  116. }
  117. ans[x]=max(tre[root[x]].len,tre[root[x]].llen+tre[root[x]].rlen);
  118. }
  119. signed main()
  120. {
  121. n=read();
  122. m=read();
  123. q=read();
  124. for(int i=1,x,y;i<n;i++)
  125. {
  126. x=read();
  127. y=read();
  128. add(x,y);
  129. fa[y]=x;
  130. }
  131. for(int i=1,x,y;i<=q;i++)
  132. {
  133. x=read();
  134. y=read();
  135. p[x].push_back(y);
  136. }
  137. dfs(1);
  138. for(int i=1;i<=n;i++)
  139. {
  140. if(!root[i]) printf("-1\n");
  141. else printf("%lld\n",ans[i]);
  142. }
  143. return 0;
  144. }

造資料必備

可以出來鏈的,菊花圖的,滿二叉樹的。。

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0,f=1;
  9. char ch=getchar();
  10. while(ch>'9'||ch<'0')
  11. {
  12. if(ch=='-') f=-1;
  13. ch=getchar();
  14. }
  15. while(ch>='0'&&ch<='9')
  16. {
  17. x=(x<<1)+(x<<3)+(ch^48);
  18. ch=getchar();
  19. }
  20. return x*f;
  21. }
  22. int random(int l,int r)
  23. {
  24. return rand()%(r-l+1)+l;
  25. }
  26. signed main()
  27. {
  28. freopen("date.in","w",stdout);
  29. srand(time(0));
  30. int n=10,m=20,q=random(1,10),opt=random(1,3);
  31. cout<<n<<' '<<m<<' '<<q<<endl;
  32. if(opt==1) for(int i=2;i<=n;i++) cout<<(i+1)/2<<' '<<i<<endl;
  33. else if(opt==2) for(int i=2;i<=n;i++) cout<<1<<' '<<i<<endl;
  34. else for(int i=2;i<=n;i++) cout<<i-1<<' '<<i<<endl;
  35. for(int i=1;i<=q;i++) cout<<random(1,n)<<' '<<random(1,m)<<endl;
  36. return 0;
  37. }

T3 Connect

解題思路

考場上想的是先跑一個最大生成樹,然後對於 n 所在的子樹上進行一些操作。

後來儘管算了一下時間複雜度小的離譜,但還是交了上去QAQ。

然後考完之後看了一眼題解:狀壓,這複雜度才對嘛。

後來下午講題的時候 戰神 拿出了他的 AC_Code。

然後瞥了一眼四周都在截圖。。。(我也。。

\(f_{sta,i}\) 表示現在狀態是 sta ,這個聯通塊(或者說是掛了一些東西的鏈)的結尾是 j 節點。

然後狀態的轉移就可以是,在這個鏈上再掛上一些聯通塊,或者在鏈的尾部新加入節點。

預處理出每一種聯通塊內部的邊的總價值。

以及某一個聯通塊向聯通塊外面的某一個點的連邊的總價值。

最後進行狀壓轉移就好了。。

code

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0,f=1;
  9. char ch=getchar();
  10. while(ch>'9'||ch<'0')
  11. {
  12. if(ch=='-') f=-1;
  13. ch=getchar();
  14. }
  15. while(ch>='0'&&ch<='9')
  16. {
  17. x=(x<<1)+(x<<3)+(ch^48);
  18. ch=getchar();
  19. }
  20. return x*f;
  21. }
  22. const int N=16;
  23. int n,m,cet[1<<N][N],edge[N][N],all[1<<N],f[1<<N][N];
  24. signed main()
  25. {
  26. n=read();m=read();
  27. for(int i=1,x,y,val;i<=m;i++)
  28. {
  29. x=read();y=read();val=read();
  30. edge[x][y]=edge[y][x]=val;
  31. }
  32. for(int i=1;i<(1<<n);i++)
  33. for(int j=1;j<=n;j++)
  34. for(int k=j+1;k<=n;k++)
  35. if((i>>j-1)&1 and (i>>k-1)&1)
  36. all[i]+=edge[j][k];
  37. for(int i=1;i<(1<<n);i++)
  38. for(int j=1;j<=n;j++)
  39. for(int k=1;k<=n;k++)
  40. if((i>>j-1)&1 and !((i>>k-1)&1))
  41. cet[i][k]+=edge[j][k];
  42. memset(f,-1,sizeof(f));
  43. f[1][1]=0;
  44. for(int i=1;i<(1<<n);i++)
  45. for(int j=1;j<=n;j++)
  46. if(f[i][j]!=-1)
  47. {
  48. for(int k=1;k<=n;k++)
  49. if(!((i>>k-1)&1) and edge[j][k])
  50. f[i|(1<<k-1)][k]=max(f[i|(1<<k-1)][k],f[i][j]+edge[j][k]);
  51. for(int k=((1<<n)-1)^i;k;k=(((1<<n)-1)^i)&(k-1))
  52. f[i|k][j]=max(f[i|k][j],f[i][j]+all[k]+cet[k][j]);
  53. }
  54. printf("%lld",all[(1<<n)-1]-f[(1<<n)-1][n]);
  55. return 0;
  56. }