CF97C Winning Strategy 构造、图论

时间:2023-03-09 13:44:22
CF97C Winning Strategy 构造、图论

题目传送门:http://codeforces.com/problemset/problem/97/C

题意:给出$n$与一个范围在$[0,1]$内的递增序列$P_0-P_n$,试构造一个无穷序列$\{a_i\}$满足$0 \leq a_i \leq n$,使得对于任意$k > 0$满足$a_k \leq \sum\limits_{i=1}^{k-1}(n - 2a_i)$且极限$\lim\limits_{m \rightarrow +\infty} \frac{\sum\limits_{i=1}^mp_{a_i}}{m}$达到最大,给出这个最大值。$N \leq 100$


因为数列无限,所以显然不能够将这个数列表示出来,故考虑构造一个循环节,使这个循环节的平均值最大,然后无限循环这个循环节,就能够得到最大的平均值。所以我们考虑找到一个有限数列使得其平均值最大。

方法一:

考虑转换模型。令$Q=\sum\limits_{i=1}^{k-1}(n - 2a_i)$,对于$a_k$,它会造成$P_{a_k}$的贡献并使得$Q+=n-2a_k=-a_k+(n-a_k)$,也就是待选的范围$Q$减掉了$a_k$,又补上了$n-a_k$。故考虑:最开始我们有一个箱子,其中有$0$个物品,第$i$次从箱子中拿出$a_i$个物品,并补进$n-a_i$个物品,求$\lim\limits_{m \rightarrow +\infty} \frac{\sum\limits_{i=1}^mp_{a_i}}{m}$的最大值。

接下来我们能发现某一个时刻的决策只与其对应的$Q$有关,与时刻无关,所以我们可以将$Q$的取值看成点,决策转换看成边,边权是对应的贡献,构出一个图。可以知道其中对应箱子中物品数量大于$2n$的点显然是没有意义的,因为从其中取出$n$个得到$P_n$的贡献仍然能够覆盖所有的$0-n$的取值。所以我们的点数只有$2n$个($0$点也是没有意义的,因为最优解一定不会出现在$0$号点)。

回到我们需要求的东西,是一个循环节,对应在图中是一个环。所以我们需要找的是图上一个平均边权最大的环。这个显然是可以二分的,check函数将所有边权减掉mid,用spfa判断正环即可。时间复杂度约为$O(N^3)$

 #include<bits/stdc++.h>
 #define R register
 #define MAXN 100010
 #define eps 1e-9
 using namespace std;

 ] , dis[];
 short N , K;
 queue < int > q;
 struct Edge{
     int end , upEd;
     double w;
 }Ed[MAXN];
 ] , flo[] , cntEd;
 ];

 inline void addEd(int a , int b , double c){
     Ed[++cntEd].end = b;
     Ed[cntEd].w = c;
     Ed[cntEd].upEd = head[a];
     head[a] = cntEd;
 }

 inline bool check(double mid){
     while(!q.empty())
         q.pop();
     memset(dis , 0xdd , sizeof(dis));
     memset(inq ,  , sizeof(inq));
     memset(flo ,  , sizeof(flo));
     dis[flo[] = ] = ;
     q.push();
     while(!q.empty()){
         int t = q.front();
         q.pop();
         inq[t] = ;
         for(int i = head[t] ; i ; i = Ed[i].upEd)
             if(dis[Ed[i].end] < dis[t] + Ed[i].w - mid){
                 dis[Ed[i].end] = dis[t] + Ed[i].w - mid;
                 flo[Ed[i].end] = flo[t] + ;
                 if(flo[Ed[i].end] > K)
                     ;
                 if(!inq[Ed[i].end]){
                     inq[Ed[i].end] = ;
                     q.push(Ed[i].end);
                 }
             }
     }
     ;
 }

 inline void solve(){
      ; i <= K ; i++)
          ; j <= K ; j++){
             int k = i - j;
                 ) == (N & ))
                     addEd(i , j , p[N + i - j >> ]);
         }
      , r = ;
     while(r - l > eps){
         ;
         check(mid) ? l = mid : r = mid;
     }
     printf("%.8lf" , l);
 }

 int main(){
     scanf("%d" , &N);
     K = N << ;
      ; i <= N ; ++i)
         scanf("%lf" , &p[i]);
     solve();
     ;
 }

方法二:

由我们的图论模型可以得出一个结论:我们只会选一种$n-2a_i>0$的$a_i$和一种$n-2a_i<0$的$a_i$。

证明:

考虑同时选取了$a_j$与$a_k$使得$n-2a_j<0$且$n-2a_k<0$,考虑进行$(n-2a_j)(n-2a_k)$的物品拿出

对于选择$j$有$P_j(n-2a_k)$的贡献,对于选择$k$有$P_k(n-2a_j)$的贡献,我们假设$P_j(n-2a_k)>P_k(n-2a_j)$

那么我们进行无限次之后,假定这个次数为$(n-2a_j)(n-2a_k)$的倍数,这样选择$j$的贡献仍然大于选择$k$的贡献,故选择$j$更好。

大于$0$的情况考虑物品贡献即可。

所以我们可以枚举较小的一个$a_i$和较大的一个$a_j$算出以它们为循环节时的答案,所有的答案取$max$即可,时间复杂度为$O(n^2)$

注意:当$n$为偶数时,$\frac{n}{2}$需要特殊判断。

 #include<bits/stdc++.h>
 using namespace std;

 ];

 int main(){
     int N;
     ;
     cin >> N;
      ; i <= N ; i++)
         cin >> p[i];
      ; i <= N -  >>  ; i++)
         ) +  ; j <= N ; j++)
              * j - N) + p[j] * (N -  * i)) / (-  * i +  * j) > ans)
                 ans = (p[i] * ( * j - N) + p[j] * (N -  * i)) / (-  * i +  * j);
     ) == )
         ans = max(ans , p[N >> ]);
     cout << ) << ans;
     ;
 }