HDU 6274 Master of Sequence 二分答案 + 优化

There are two sequences a1, a2, · · · , an, b1, b2, · · · , bn. Let S(t) = ∑n
i=1⌊
t−bi
ai
⌋. There are m operations
within three kinds as following:
• 1 x y: change value ax to y.
• 2 x y: change value bx to y.
• 3 k: ask min{t|k ≤ S(t)}
Input
The first line contains a integer T (1 ≤ T ≤ 5) representing the number of test cases.
For each test case, the first line contains two integers n (1 ≤ n ≤ 100000), m (1 ≤ m ≤ 10000).
The following line contains n integers representing the sequence a1, a2, · · · , an.
The following line contains n integers representing the sequence b1, b2, · · · , bn.
The following m lines, each line contains two or three integers representing an operation mentioned
above.
It is guaranteed that, at any time, we have 1 ≤ ai ≤ 1000, 1 ≤ bi
, k ≤ 109
. And the number of
queries (type 3 operation) in each test case will not exceed 1000.
Output
For each query operation (type 3 operation), print the answer in one line.


训练赛的时候想到了用二分答案做,但是一直没有优化的想法,这次也算是接触了一次全新的优化思想吧…

首先我们为了优化S(t)函数,需要将t和 b i b_i bi从向下取整的式子中取出来。
这里可以令 t = k 1 i ∗ a i + c 1 i , b i = k 2 i ∗ a i + c 2 i t=k_{1i}*a_i+c_{1i},b_i=k_{2i}*a_i+c_{2i} t=k1iai+c1i,bi=k2iai+c2i,这样我们便可以将S(t)函数优化成

S ( t ) = K 1 − K 2 + ∑ i = 1 n ⌊ c 1 i − c 2 i a i ⌋ S(t)=K1-K2+\sum_{i=1}^{n}\lfloor\frac{c_{1i}-c_{2i}}{a_i}\rfloor S(t)=K1K2+i=1naic1ic2i

其中

K 1 = ∑ i = 1 n k 1 i = ∑ i = 1 n ⌊ t a i ⌋ K1 = \sum_{i=1}^{n}k_{1i}=\sum_{i=1}^{n}\lfloor\frac{t}{a_i}\rfloor K1=i=1nk1i=i=1nait
K 2 = ∑ i = 1 n k 2 i = ∑ i = 1 n ⌊ b i a i ⌋ K2 = \sum_{i=1}^{n}k_{2i}=\sum_{i=1}^{n}\lfloor\frac{b_i}{a_i}\rfloor K2=i=1nk2i=i=1naibi

K1的计算可以通过对 a i a_i ai进行预处理来完成,因为 a i ≤ 1000 a_i\le1000 ai1000,那么只要用一个数组记录 a a a中每个数字的出现次数,就可以通过 ∑ i = 1 1000 t / i ∗ c n t [ i ] \sum_{i=1}^{1000}t/i*cnt[i] i=11000t/icnt[i]便可以计算K1了。

K2可以在输入的时候进行预处理,再进行操作1、2时,通过 K 2 = K 2 − ⌊ b i a i ⌋ + ⌊ b i y ⌋ K2=K2-\lfloor\frac{b_i}{a_i}\rfloor+\lfloor\frac{b_i}{y}\rfloor K2=K2aibi+ybi K 2 = K 2 − ⌊ b i a i ⌋ + ⌊ y a i ⌋ K2=K2-\lfloor\frac{b_i}{a_i}\rfloor+\lfloor\frac{y}{a_i}\rfloor K2=K2aibi+aiy进行复杂度为O(1)的更新。

最后是 ∑ i = 1 n ⌊ c 1 i − c 2 i a i ⌋ \sum_{i=1}^{n}\lfloor\frac{c_{1i}-c_{2i}}{a_i}\rfloor i=1naic1ic2i,我们知道 c 1 i , c 2 i < a i c_{1i},c_{2i}<a_i c1i,c2i<ai,则

⌊ c 1 i − c 2 i a i ⌋ = 0 \lfloor\frac{c_{1i}-c_{2i}}{a_i}\rfloor=0 aic1ic2i=0        c 1 i ≥ c 2 i c_{1i}\ge c_{2i} c1ic2i
⌊ c 1 i − c 2 i a i ⌋ = − 1 \lfloor\frac{c_{1i}-c_{2i}}{a_i}\rfloor=-1 aic1ic2i=1     c 1 i < c 2 i c_{1i}< c_{2i} c1i<c2i

所以我们需要对 c 1 i < c 2 i c_{1i}<c_{2i} c1i<c2i的情况进行讨论,也就是 t % a i < b i % a i t\%a_i<b_i\%a_i t%ai<bi%ai的情况。这里可以使用一个二维数组,arr[x][y]表示满足 a i = x a_i=x ai=x b i % a i ≥ y b_i\%a_i\ge y bi%aiy的个数,同时K1的计算也可以通过arr[x][0]来获得a数组中等于x的数量。所以最后一部分的值为 ∑ i = 1 1000 a r r [ i ] [ t % i + 1 ] \sum_{i=1}^{1000}arr[i][t\%i+1] i=11000arr[i][t%i+1]

在可以快速求出S(t)后,从1到1e13求一遍二分答案即可。

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

const int N = 1e5 + 10;
int t,n,m,x,y,k,op;
int a[N],b[N],book[1005][1005];
long long K1,K2;
bool judge(long long mid){
    long long res = 0;
    for(int i = 1;i<=1000;i++){				//求出每个t值对应的S(t)函数值,并判断是否符合条件
        res += mid/i*book[i][0];	
        res -= book[i][mid%i+1];
    }
    return k<=res-K2;
}
int main() {
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--){
        cin>>n>>m;
        K2 = 0;
        memset(book,0,sizeof(book));
        for(int i = 1;i<=n;i++){
            cin>>a[i];
        }
        for(int i = 1;i<=n;i++){
            cin>>b[i];
            K2 += b[i]/a[i];
            book[a[i]][b[i]%a[i]]++;		//预处理
        }
        for(int i = 1;i<=1000;i++){
            for(int j = i-1;j>=0;j--){
                book[i][j] += book[i][j+1];
            }
        }
        while(m--){
            cin>>op;
            if(op==1){
                cin>>x>>y;
                for(int i = b[x]%a[x];i>=0;i--){
                    book[a[x]][i]--;
                }
                for(int i = b[x]%y;i>=0;i--){
                    book[y][i]++;
                }
                K2 -= (b[x]/a[x]);
                K2 += (b[x]/y);
                a[x] = y;
            }else if(op==2){
                cin>>x>>y;
                for(int i = b[x]%a[x];i>=0;i--){
                    book[a[x]][i]--;
                }
                for(int i = y%a[x];i>=0;i--){
                    book[a[x]][i]++;
                }
                K2 -= (b[x]/a[x]);
                K2 += (y/a[x]);
                b[x] = y;
            }else{
                cin>>k;
                long long l = 1,r = 1e13,res = 0;	
                while(l<=r){							//二分答案
                    long long mid = (l+r)/2;
                    if(judge(mid)){
                        res = mid;
                        r = mid-1;
                    }else{
                        l = mid+1;
                    }
                }
                cout<<res<<'\n';
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值