【srm603】Sum Of Arrays

Description:

英雄有两个整数数列A 和B。每个数列有恰好n 个元素。

英雄将会对两个数列进行两次操作:首先,他会将两个数列中的元素按照某种方式重新排列。(他可以将数列排成任意他想要的顺序,包括原本的顺序。交换数列A 和B 的元素是不被允许的,每个数列要独自重排。)在此之后,他会计算出一个有n 个元素的新数列C,按照对于所有i,C[i] = A[i] + B[i]。

英雄喜欢相同的多个数字。他的目标是让C 里面产生尽可能多的相同数字。更正式地说,对于一个给定的C,他对满足下述条件的整数X 和Y 感兴趣:值等于Y 的元素在C 中出现了X 次,并且X 需尽可能的大。

你会获得整数n 以及用于产生数列A 和B 的随机种子。每一组随机种子有六个整数,用a0, a1,… ,a5 以及b0, b1,…,b5表示。算出数列A 和B 之后,你的任务是计算可能的最佳整数对(X,Y )。也就是说,X 必须尽可能地大,并且,如果有多个这样的整数对,那么Y 也必须尽可能地大。

数列A 按照如下定义生成:

•A[0] = a0

•A[1] = a1

•对于在2 到n-1之间的i,有A[i] = (A[i-1]* a2 + A[i-2]*a3 + a4)mod a5

同理,数列B 由类似的定义生成。

3<= n <=100, 000, 2 <=a5, b5 <=100, 000。

题解:

cidi 分别表示a=i的个数和b=i的个数。

显然 ansj=ji=0min(ci,dji)

取min是不好算的,可是这题比较特殊。

那就是 c d <=n <script type="math/tex" id="MathJax-Element-484"><=n</script>

这启发我们分块。

设一个匝值L。

枚举i=1->L,i的含义为min值。

如果c[i]>=i设为1,否则为0,d同理。

这样FFT一波可以求出,min值为i的答案。

这一部分复杂度为 O(n log n L)

对于大于L的,直接暴算,复杂度小于 O(n2L2)

L取10的时候比较优秀。

Code:

#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define ff(i, x, y) for(int i = x; i < y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 4e5 + 5;

int n, m, a[N], b[N], c[N], d[N], e[N], f[N];
ll ans[N];
ll a0, a1, a2, a3, a4, a5, b0, b1, b2, b3, b4, b5;

const int mo = 998244353;
ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y /= 2, x = x * x % mo)
        if(y & 1) s = s * x % mo;
    return s;
}
ll w[N], tx;
void dft(int *a, int n) {
    ff(i, 0, n) {
        int p = i, q = 0;
        ff(j, 0, tx) q = q * 2 + p % 2, p /= 2;
        if(q > i) swap(a[q], a[i]);
    }
    for(int m = 2; m <= n; m *= 2) {
        int h = m / 2;
        ff(i, 0, h) {
            ll W = w[i * (n / m)];
            for(int j = i; j < n; j += m) {
                int k = j + h;
                int u = a[j], v = (ll) a[k] * W % mo;
                a[j] = (u + v) % mo, a[k] = (u - v + mo) % mo;
            }
        }
    }
}
void fft(int *a, int *b, int n) {
    ll rev = ksm(3, (mo - 1) / n);
    w[0] = 1; fo(i, 1, n) w[i] = w[i - 1] * rev % mo;
    dft(a, n); dft(b, n);
    ff(i, 0, n) a[i] = (ll) a[i] * b[i] % mo;
    fo(i, 0, n / 2) swap(w[i], w[n - i]);
    dft(a, n); ll ni = ksm(n, mo - 2);
    ff(i, 0, n) a[i] = (ll) a[i] * ni % mo;
}

int d1[N], d2[N];

int main() {
    freopen("sum.in", "r", stdin);
    freopen("sum.out", "w", stdout);
    for(;scanf("%d", &n) != EOF; ) {
        scanf("%lld %lld %lld %lld %lld %lld", &a0, &a1, &a2, &a3, &a4, &a5);
        scanf("%lld %lld %lld %lld %lld %lld", &b0, &b1, &b2, &b3, &b4, &b5);
        a[0] = a0; a[1] = a1;
        fo(i, 2, n - 1) a[i] = (a2 * a[i - 1] + a3 * a[i - 2] + a4) % a5;
        b[0] = b0; b[1] = b1;
        fo(i, 2, n - 1) b[i] = (b2 * b[i - 1] + b3 * b[i - 2] + b4) % b5;
        m = max(a5, b5); tx = 0; while(1 << tx ++ < m); m = 1 << tx;
        ff(i, 0, m) ans[i] = c[i] = d[i] = 0;
        fo(i, 0, n - 1) c[a[i]] ++, d[b[i]] ++;
        fo(l, 1, 10) {
            ff(i, 0, m) e[i] = c[i] >= l, f[i] = d[i] >= l;
            fft(e, f, m);
            ff(i, 0, m) ans[i] += e[i];
        }
        d1[0] = d2[0] = 0;
        ff(i, 0, m) {
            if(c[i] > 10)
                d1[++ d1[0]] = i;
            if(d[i] > 10)   
                d2[++ d2[0]] = i;
        }
        fo(i, 1, d1[0]) fo(j, 1, d2[0]) {
            int x = d1[i], y = d2[j];
            ans[x + y] += min(c[x], d[y]) - 10;
        }
        ll x = 0, y = 0;
        fd(i, m, 0)
            if(ans[i] > x)
                x = ans[i], y = i;
        printf("%lld %lld\n", x, y);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值