Loading [MathJax]/jax/element/mml/optable/BasicLatin.js

Codeforces 1500F. Cupboards Jumps

题目链接:1500F - Cupboards Jumps

题目大意:给出一个长度为n2的数组w,要求构造出一个各元素取值在[0,1018]范围内,长度为n的数组h,使得对任意i[1,n2],有wi=max。保证3\le n \le 10^6,0\le w_i\le C \le 10^{12}

题解:首先考虑一个转换,三个数字之间的最大值减最小值其实就是两两做差的最大值,因此有w_i = \max(|h_i-h_{i+1}|,|h_{i+1}-h_{i+2}|,|h_i-h_{i+2}|)

   我们令d_{i}=h_{i+1}-h_{i},代入上式就能得出w_i = \max(|d_i+d_{i+1}|,|d_i|,|d_{i+1}|)。显然就有|d_i|\le w_i \le C

   设f[i][j]表示是否存在一种方案,使得d_i=j且对于前i个位置的d_i均满足条件,我们就能得到一个O(nC)的DP做法(确定f[i][j]=1时,对于w_i的限制,可以得出f[i+1][L...R]是可以满足条件的,在下一次循环就可以用前缀和的方式逐一赋值并判断是否满足下一个w的条件)。另外我们可以发现,若存在一个d_i=j的合法方案,那么一定也存在一个d_i=-j的合法方案(可以选择将前面所有的d都取反),因此第二维的取值就可以限制在[0,C]的范围内。

   从上述的DP过程中可以发现,如果把每个i合法的点都看成若干个区间,我们通过题目的一些条件,考虑式子在什么时候和w_i取等号,可以得到如下的转移方式:

  • 首先,如果w_i存在于d_{i}的合法取值范围内,那么对于d_{i+1}[0,w_i]均合法,且不需要再考虑其他情况。(w_i=|d_i|
  • 如果存在一个小于w_i的数x使得f[i][x]=1,即x存在于某一区间内,那么w_i一定是一个合法的取值(令d_i=-x,d_{i+1}=w_i),相当于一个合法区间[w_i,w_i]。(w_i=|d_{i+1}|
  • 剩下还有一种情况,就是w_i=|d_i+d_{i+1}|,那么对当前任意一个合法的区间[l,r],就能推出w_i=|d_i+d_{i+1}|\le |d_i|+|d_{i+1}|, |d_{i+1}|\ge w_i-|d_i|\ge w_i-r,且有 |d_{i+1}|=w_i-|d_i|\le w_i-l,由此可以推出另一个合法区间[w_i-r,w_i-l]

   在上述转移过程中,每轮转移至多只会产生一个新的合法区间,如果我们暴力维护这些区间,就在O(n^2)的时间复杂度内,完成对每个位置的合法区间的判断,值得注意的是每次开始转移前,要把当前的所有合法区间与[0,w_i]取一次交。

   现在考虑如何优化这个区间转移的过程。

   回顾转移的方式,我们发现,当出现了第一种情况时,所有的区间都会被统一为一个区间[0,w_i],这是我们最乐意看到的。而当出现了其他情况时,都是将当前区间以\frac{w_i}{2}为中心进行一个反转,并出现一个新的单点。而这个单点在之后的转移过程中,也始终会是一个单点,并且也会跟着翻转,而区间数目在整个过程中有且只有一个。于是我们开始考虑不再翻转这一个个点,而是考虑翻转整个数轴。为了统一,我们一律对数轴以零点为中心进行翻转,并记录当前数轴是正着的还是倒着的(即记录翻转次数的奇偶性),此外还要记录当前数轴被平移了多少个单位长度,用两个量来确定当前数轴的变换量。

   接下来,我们需要倒着求出一个合法方案,确定所有d_i的绝对值。同样地,我们还是考虑式子在什么时候和w_i取等号,得到如下的转移方式(若未说明默认d外面有绝对值符号):

  • w_i=d_i:这个很好判断,只要w_id_i的合法区间内,直接令d_i=w_i即可。
  • w_i=d_{i+1}:在转移过程中,如果当前位置的值已经和w_i相等,那么d_i可以取合法区间内的任意一个值。
  • 如果上述两种条件均不能满足,由当前取值说明当前d_{i+1}的取值是通过w_i=|d_i+d_{i+1}|转移过来的,因此令d_i=w_i-d_{i+1}即可。

   于是我们发现,在倒推的过程中,我们只需要记下每个d_i是否可以取到w_i,以及每个d_i的所有合法取值中的任意一个即可。在实现的时候,设这个合法值为v_i,那么在存储的时候优先令v_i=w_i(如果可以的话)就可以方便判断。

   确定完所有|d_i|的值后,我们需要确定他们的符号,显然,除了|d_i|+|d_{i+1}|=w_i时他们可以同号外,其余情况必须异号,否则就会出现加起来的绝对值超出d_i的情况。

   由于存储单点时需要用到set,因此总时间复杂度为O(n\log n)

 

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 1000010
 4 #define LL long long
 5 LL n,C,w[N],d[N],v[N],l,r,o,k;
 6 set<LL>s;
 7 LL f(LL x){return o&1?k-x:k+x;}
 8 LL g(LL y){return o&1?k-y:y-k;}
 9 int main()
10 {
11     scanf("%lld%lld",&n,&C);
12     l=0,r=C;
13     for(LL i=1;i<=n-2;i++){
14         scanf("%lld",&w[i]);
15         LL L=g(0),R=g(w[i]);
16         if(L>R)swap(L,R);
17         l=max(l,L),r=min(r,R);
18         while(!s.empty() && (*s.begin())<L)s.erase(s.begin());
19         while(!s.empty() && (*s.rbegin())>R)s.erase(*s.rbegin());
20         if(s.empty() && l>r)return printf("NO\n"),0;
21         LL W=g(w[i]);
22         if(s.count(W) || (l<=W && W<=r)){
23             l=0,r=v[i]=w[i];
24             s.clear();
25             o=k=0;
26             continue;
27         }
28         if(l<=r)v[i]=f(l);
29         else v[i]=f(*s.begin());
30         o^=1,k=w[i]-k;
31         s.insert(g(w[i]));
32     }
33     if(l<=r)d[n-1]=f(l);
34     else d[n-1]=f(*s.begin());
35     for(LL i=n-2;i>=1;i--){
36         if(v[i]==w[i]){
37             d[i]=w[i];
38             continue;
39         }
40         if(d[i+1]==w[i]){
41             d[i]=v[i];
42             continue;
43         }
44         d[i]=w[i]-d[i+1];
45     }
46     k=1;
47     for(LL i=n-2;i>=1;i--){
48         if(abs(d[i])+abs(d[i+1])!=w[i])k*=-1;
49         d[i]=k*d[i];
50     }
51     LL mn=0;
52     for(LL i=1;i<=n-1;i++)d[i]+=d[i-1],mn=min(mn,d[i]);
53     printf("YES\n");
54     for(LL i=1;i<=n;i++)printf("%lld%c",d[i-1]-mn,i<n?' ':'\n');
55 }
View Code
复制代码

 

posted @   DeaphetS  阅读(265)  评论(2编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示