[atAGC054F]Decrement

令$a_{i}$和$b_{i}$分别为$A_{i}$和$B_{i}$减少的值,考虑判定$\{a_{i}\},\{b_{i}\}$能否被得到

结论:$\{a_{i}\},\{b_{i}\}$能否被得到当且仅当满足以下条件——

1.$0\le a_{i}\le A_{i}$,$0\le b_{i}\le B_{i}$

2.$a_{i}+b_{i-1}+b_{i}$为偶数(其中$b_{0}=b_{n}=0$)

3.$a_{i},b_{i-1},b_{i}$这三个数中不存在一个数大于另外两数之和

必要性——第1个条件由非负的限制显然,第2个和第3个条件均考虑$a_{i},b_{i-1},b_{i}$这三个数中,每一次对其中一个数+1,另外两数中必然恰有一个数+1,显然具有上述性质

充分性——归纳,并考虑最后一次操作,取第一个$a_{i}>0$的位置和其之后第一个$a_{j}=b_{j-1}+b_{j}$的位置即可(具体存在性和正确性可以自行分析)

对于$A_{i}+B_{i-1}<B_{i}$,注意到$0\le b_{i}\le B_{i}$即为第3个条件的必要条件,可以忽略此条件,因此不妨调整为$B_{i}=A_{i}+B_{i-1}$(类似地,也可以对$B_{i-1}$做此操作)

重复此过程(可以用类似dijkstra的贪心来做),即有$|B_{i}-B_{i-1}|\le A_{i}$

此时,对于$A_{k}\ge B_{k-1}+B_{k}$(其中$2\le k\le n-1$),同样可以忽略$0\le a_{k}\le A_{k}$的条件,因此操作$(i,j)$(其中$i<k<j$)不妨改为操作$(i,k)$和$(k,j)$,这样只让$A_{k}$额外减小2(仍合法)且操作数增加

换言之,不存在这样的操作,即不妨变为$[1,k]$和$[k,n]$这两个子问题

重复此过程,考虑最终的子问题,即有$\forall 2\le i\le n-1,|B_{i}-B_{i-1}|\le A_{i}<B_{i-1}+B_{i}$,且类似前面的,不妨再令$A_{1}=B_{1},A_{n}=B_{n-1}$

(注意这个过程的实现并不需要递归,直接从前往后枚举并划分即可)

另一方面,注意到操作次数即为$\frac{\sum_{i=1}^{n}a_{i}}{2}$,同时$\{a_{i}\}$与最终的$\{A_{i}\}$一一对应,因此从$\{a_{i}\}$的角度来看,问题即求有多少组$\{a_{i}\}$满足$\frac{\sum_{i=1}^{n}a_{i}}{2}$取到最大值且$\exists \{b_{i}\}$满足结论中的条件

考虑从前往后依次填$b_{i}$,并要求$\forall 1\le j<i,a_{j}=A_{j}$,求出此时$b_{i}$可行的范围

若已经确定$b_{i-1}$,则$b_{i}$的范围为$\{x\mid x\in [|{A_{i}-b_{i-1}}|,\min(A_{i}+b_{i-1},B_{i})]$且$x\equiv a_{i}+b_{i-1}(mod\ 2)\}$,那么即要将$b_{i-1}$的范围内所有值代入并求并

归纳最终的形式为$\{x\mid x\in [L,B_{i}]$且$x\equiv p(mod\ 2)\}$且该集合非空(也即必然包含$B_{i}-1$或$B_{i}$),转移可以自行分类讨论得到(式子较为复杂),进而不难证明归纳

以此法构造,即$a_{1},a_{2},...,a_{n-1}$都可以取到$A_{i}$,且$a_{n}$可以取到$A_{n}-1$或$A_{n}$,显然$\sum_{i=1}^{n}a_{i}\equiv 0(mod\ 2)$,因此$a_{n}$的取值可以直接确定,进而不难发现$\frac{\sum_{i=1}^{n}a_{i}}{2}$已经取到上限

换言之,若$\sum_{i=1}^{n}A_{i}$为偶数,则取到最大值仅有$a_{i}=A_{i}$时(方案数为1),否则即对每一个$i$判断是否可以令$a_{i}=A_{i}-1$且$\forall j\ne i,a_{j}=A_{j}$,按之前的方式求出$B_{i-1}$和$B_{i}$的区间即可

时间复杂度为$o(n\log n)$(调整$B_{i-1}$和$B_{i}$),可以通过

(代码中转移的部分有点丑)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define mod 998244353
 5 #define ll long long
 6 priority_queue<pair<int,int> >q;
 7 int n,ans,a[N],b[N],vis[N],L[N],p[N];
 8 int get(int k,int p){
 9     if ((k&1)!=p)k--;
10     return k;
11 }
12 int calc(int x,int y){
13     a[x]=b[x],a[y]=b[y-1];
14     int s=0;
15     for(int i=x;i<=y;i++)s^=(a[i]&1);
16     if (!s)return 1;
17     L[x-1]=p[x-1]=0;
18     for(int i=x;i<=y;i++){
19         p[i]=(p[i-1]^(a[i]&1));
20         if (L[i-1]>=a[i])L[i]=L[i-1]-a[i];
21         else L[i]=get(max(a[i]-get(b[i-1],p[i-1]),0)+1,p[i]);
22     }
23     int LL=0,pp=0,ans=0;
24     for(int i=y;i>=x;i--){
25         int x=get(b[i-1],p[i-1]),y=get(b[i],pp);
26         if (a[i]-1<=x+y){
27             if (abs(x-y)<=a[i]-1)ans++;
28             else{
29                 if (a[i]-1<=x+y-2){
30                     if ((x>y)&&(L[i-1]<=x-2))ans++;
31                     if ((x<y)&&(LL<=y-2))ans++;
32                 }
33             }
34         }
35         if (LL>=a[i])LL-=a[i];
36         else LL=get(max(a[i]-get(b[i],pp),0)+1,(pp^(a[i]&1)));
37         pp^=(a[i]&1);
38     }
39     return ans;
40 }
41 int main(){
42     scanf("%d",&n);
43     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
44     for(int i=1;i<n;i++)scanf("%d",&b[i]);
45     for(int i=0;i<=n;i++)q.push(make_pair(-b[i],i));
46     while (!q.empty()){
47         int k=q.top().second;
48         q.pop();
49         if (vis[k])continue;
50         vis[k]=0;
51         if ((k)&&(b[k-1]>a[k]+b[k])){
52             b[k-1]=a[k]+b[k];
53             q.push(make_pair(-b[k-1],k-1));
54         }
55         if ((k<n)&&(b[k+1]>a[k+1]+b[k])){
56             b[k+1]=a[k+1]+b[k];
57             q.push(make_pair(-b[k+1],k+1));
58         }
59     }
60     int lst=ans=1;
61     for(int i=2;i<=n;i++)
62         if (a[i]>=b[i-1]+b[i]){
63             int x=b[i];
64             b[i]=0,ans=(ll)ans*calc(lst,i)%mod;
65             lst=i,b[i-1]=0,b[i]=x;
66         }
67     printf("%d\n",ans);
68     return 0;
69 } 
View Code

 

posted @ 2021-10-05 22:58  PYWBKTDA  阅读(83)  评论(0编辑  收藏  举报