• 2019牛客暑期多校训练营(第九场)


    2019牛客暑期多校训练营(第九场)

    题目链接

    A.The power of Fibonacci

    注意到模数为合数,并且可以拆为(2^9,5^9),这样就相当于将原问题拆解成了规模比较小的情况。
    通过(2^9,5^9)分别求出循环节,找到问题的解,之后(CRT)合并即可。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    struct CRT{
        void exgcd(ll a, ll b, ll &g, ll &x, ll &y) {
            if(b == 0) {
                x = 1, y = 0, g = a;
                return ;
            }
            exgcd(b, a % b, g, x, y);
            int t = x;
            x = y;
            y = t - (a / b) * y;
        }
        ll china(ll m[], ll a[], int n) {
            ll M, Mi, d, X, Y, ans;
            M = 1; ans = 0;
            for(int i = 1; i <= n; i++) M *= m[i];
            for(int i = 1; i <= n; i++) {
                Mi = M / m[i];
                exgcd(Mi, m[i], d, X, Y);
                ans = (ans + Mi * X * a[i]) % M;
            }
            if(ans < 0) ans += M;
            return ans;
        }
    }crt;
    const int MOD = 1000000000, N = 8000005;
    ll md[3] = {0, 512, 1953125};
    ll f[N];
    ll sum1[N], sum2[N];
    ll qp(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return ans;
    }
    ll n, m;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        f[0] = 0; f[1] = f[2] = 1;
        sum1[1] = sum2[1] = 1; sum1[2] = sum2[2] = 2;
        int p1, p2;
        for(int i = 3;; i++) {
            f[i] = (f[i - 2] + f[i - 1]) % md[1];
            sum1[i] = (sum1[i - 1] + qp(f[i], m)) % md[1];
            if(f[i - 1] == 1 && f[i] == 0) {
                p1 = i; break;
            }
        }
        for(int i = 3;; i++) {
            f[i] = (f[i - 2] + f[i - 1]) % md[2];
            sum2[i] = (sum2[i - 1] + qp(f[i], m)) % md[2];
            if(f[i - 1] == 1 && f[i] == 0) {
                p2 = i; break;
            }
        }
        ll a[3];
        a[1] = (n / p1) * sum1[p1 - 1] % MOD; a[1] += sum1[n % p1];
        a[2] = (n / p2) * sum2[p2 - 1] % MOD; a[2] += sum2[n % p2];
        ll ans = crt.china(md, a, 2);
        cout << ans;
        return 0;
    }
    
    

    B.Quadratic equation

    首先将问题转化为在模意义下求解二次方程。
    方程为:(x^2-bx+cequiv 0(\% p)),配方法将式子化为:((x-frac{b}{2})^2equiv frac{b^2}{4}-c(\% p))
    之后就是二次剩余板子了。
    这里有个结论比较方便,就是当(p\% 4=3)时,方程(x^2equiv d(\% p))的解为(x=pm d^{frac{p+1}{4}})

    Code
    #include <bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    using namespace std;
    typedef long long ll;
    const int P = 1000000007, inv = 500000004;
    ll x, y, b, c;
    int T;
    template<class T>il T read(){
        rg T data=0,w=1;
        rg char ch=getchar();
        while(!isdigit(ch)){
            if(ch=='-') w=-1;
            ch=getchar();
        }
        while(isdigit(ch))
            data=data*10+ch-'0',ch=getchar();
        return data*w;
    }
    template<class T>il T read(rg T&x){
        return x=read<T>();
    }
    ll qp(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % P;
            a = a * a % P;
            b >>= 1;
        }
        return ans;
    }
    ll n;
    ll add(ll x,ll y){
        return (x+y)%P;
    }
    ll mul(ll x,ll y){
        return x*y%P;
    }
    ll Pow(ll x,ll k){
        x%=P,k%=(P-1);
        ll re=1;
        for(;k;k>>=1,x=mul(x,x))
            if(k&1) re=mul(re,x);
        return re;
    }
    ll omega;
    struct Complex{ll a,b;};
    Complex operator*(co Complex&x,co Complex&y){
        return (Complex){add(mul(x.a,y.a),mul(x.b,mul(y.b,omega))),add(mul(x.a,y.b),mul(x.b,y.a))};
    }
    Complex operator^(Complex x,ll k){
        Complex re=(Complex){1,0};
        for(;k;k>>=1,x=x*x)
            if(k&1) re=re*x;
        return re;
    }
    ll Sqrt(ll n){
        if(P==2) return 1;
        n%=P;
        if(!n) return 0;
        ll a=rand()%P;
        while(Pow(add(mul(a,a),P-n),(P-1)/2)!=P-1)
            a=rand()%P;
        omega=add(mul(a,a),P-n);
        return ((Complex){a,1}^(P+1)/2).a;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        srand(time(NULL));
        while(T--) {
            cin >> b >> c;
            ll d = ((b * b - 4 * c) % P + P) % P * qp(4, P - 2) % P;
            //v^2 = d (P P)
            if(d && qp(d, (P - 1) / 2) != 1) {
                cout << -1 << ' ' << -1 << '
    ';
            } else {
                ll ans1=Sqrt(d),ans2=P-ans1;
                if(ans1>ans2) std::swap(ans1,ans2);
                x = ans1, y = ans2;
                x = (x + b * inv % P) % P;
                y = (y + b * inv % P) % P;
                if(x > y) swap(x, y);
                cout << x << ' ' << y << '
    ';
            }
        }
    
        return 0;
    }
    
    

    C.Inversions of all permutations

    前置技能:高斯二次项系数
    这个题是wiki里面的一个推广,求的是任意多重集的情况下的解。
    解决方法是从小到大一个一个求,并且每次将答案乘起来。
    可以类比一下普通生成函数,意义比较类似。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 100005, MOD = 1000000007;
    int n;
    ll b;
    set <int> s;
    int cnt[N];
    ll sum[N], pre[N];
    ll qp(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return ans;
    }
    ll C(int n, int m) {
        if(m > n) return 0;
        return pre[n] * qp(pre[m], MOD - 2) % MOD * qp(pre[n - m], MOD - 2) % MOD;
    }
    void init(ll q) {
        int x = 1; pre[0] = 1;
        for(int i = 1; i < N; i++) {
            sum[i] = (sum[i - 1] + x) % MOD;
            x = x * q % MOD;
        }
        for(int i = 1; i < N; i++) {
            pre[i] = pre[i - 1] * sum[i] % MOD;
        }
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> b;
        for(int i = 1; i <= n; i++) {
            int a; cin >> a;
            cnt[a]++;
            s.insert(a);
        }
        init(b);
        ll ans = 1;
        int tot = 0;
        for(auto it : s) {
            ans *= C(tot + cnt[it], cnt[it]); ans %= MOD;
            tot += cnt[it];
        }
        cout << ans;
        return 0;
    }
    
    

    D.Knapsack Cryptosystem

    分两半处理,首先预处理一半的答案,然后枚举另外一半。

    Code
    #include <bits/stdc++.h>
    #include<unordered_map>
    using namespace std;
    typedef long long ll;
    const int MAXN = 1e6 + 5, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
    const ll inf = 1000000000000000010LL, INFL = 0x3f3f3f3f3f3f3f3f;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define random(a,b) ((a)+rand()%((b)-(a)+1))
     
    int n;
    ll m, a[MAXN], sum;
    unordered_map<ll, int> mp;
    void print(int x) {
        while (x) {
            cout << (x & 1);
            x >>= 1;
        }
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        for (int i = 1; i <= n; i++)cin >> a[i];
        int sz = n / 2, sz2 = n - sz;
        for (int i = 0; i < (1 << sz); i++) {
            sum = 0;
            for (int j = 0; j < sz; j++) {
                if (i >> j & 1)sum += a[j + 1];
            }
            mp[sum] = i;
        }
        for (int i = 0; i < (1 << sz2); i++) {
            sum = 0;
            for (int j = 0; j < sz2; j++) {
                if (i >> j & 1)sum += a[sz + j + 1];
            }
            if (sum < m && mp.count(m - sum)) {
                int x = mp[m - sum];
                for (int j = 0; j < sz; j++) {
                    cout << ((x >> j) & 1);
                }
                x = i;
                for (int j = 0; j < sz; j++) {
                    cout << ((x >> j) & 1);
                }
                break;
            }
        }
        return 0;
    }
    

    E.All men are brothers

    开始局面的答案易求。
    对于之后每次的连边操作,考虑答案减少的个数。
    假设现在已有(k)个连通块,我们现在将(a_i)(a_j)两个连通块相连接,那么答案减少的个数就是(sz[a_i]*sz[a_j]*C(others,2)),发现有些连通块会重复选择集合内部的数,所以还要减去那些重复的,我们这里用一个变量维护一下即可。
    所以最终答案即为(sz[a_i]*sz[a_j]*(C(others,2)-sum))

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef __int128 ll;
    const int N = 100005, M = 200005;
    struct Istream {
    	template <class T>
    	Istream &operator >>(T &x) {
    		static char ch;static bool neg;
    		for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
    		for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
    		x=neg?-x:x;
    		return *this;
    	}
    }fin;
    
    struct Ostream {
    	template <class T>
    	Ostream &operator <<(T x) {
    		x<0 && (putchar('-'),x=-x);
    		static char stack[233];static int top;
    		for(top=0;x;stack[++top]=x%10+'0',x/=10);
    		for(top==0 && (stack[top=1]='0');top;putchar(stack[top--]));
    		return *this;
    	}
    
    	Ostream &operator <<(char ch) {
    		putchar(ch);
    		return *this;
    	}
    }fout;
    ll C2(int n) {
        return (ll)n * (n - 1) / 2;
    }
    ll C3(int n) {
        return (ll)n * (n - 1) * (n - 2) / 6;
    }
    ll C4(int n) {
        return (ll)n * (n - 1) * (n - 2) * (n - 3) / 24;
    }
    int sz[N], f[N];
    int n, m;
    int find(int x) {
        return f[x] == x ? f[x] : f[x] = find(f[x]);
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        for(int i = 1; i <= n; i++) sz[i] = 1, f[i] = i;
        ll ans = C4(n), sum = 0;
        fout << ans << '
    ';
        for(int i = 1; i <= m; i++) {
            int x, y; cin >> x >> y;
            int fx = find(x), fy = find(y);
            if(fx == fy) {}
            else {
                ll tmp = sum;
                tmp -= C2(sz[fx]) + C2(sz[fy]);
                ans -= (ll)sz[fx] * sz[fy] * (C2(n - sz[fx] - sz[fy]) - tmp);
                sum -= C2(sz[fy]) + C2(sz[fx]);
                sz[fy] += sz[fx];
                f[fx] = fy;
                sum += C2(sz[fy]);
            }
            fout << ans << '
    ';
        }
        return 0;
    }
    
    

    H.Cutting Bamboos

    因为总切割次数确定,并且每次切割相同的数值,所以在切割(x)之后,剩下树枝高度总和是已知的。
    最直接的想法就是二分一个高度,然后找多少个树枝高度小于二分值,算上它们的贡献,其余数枝的贡献就为该高度。
    问题的关键就是查询区间小于(mid)的个数已经其权值和,这可以用主席树解决。
    进一步思考,发现二分都可以不用,直接上主席树即可,在主席树上面二分。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 200005;
    int n, q;
    int h[N];
    ll sum[N];
    int rt[N], lc[N * 22], rc[N * 22], tot;
    ll sumv[N * 22], sz[N * 22];
    void build(int &o, int l, int r) {
        o = ++tot;
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(lc[o], l, mid); build(rc[o], mid + 1, r);
    }
    int L, R;
    void insert(int &o, int last, int l, int r, int v) {
        o = ++tot;
        lc[o] = lc[last]; rc[o] = rc[last];
        sumv[o] = sumv[last] + v; sz[o] = sz[last] + 1;
        if(l == r) return ;
        int mid = (l + r) >> 1;
        if(v <= mid) insert(lc[o], lc[last], l, mid, v);
        else insert(rc[o], rc[last], mid + 1, r, v);
    }
    double query(int o, int last, int l, int r, double need, int cnt, ll Sum) {
        if(l == r) {
            cnt += sz[lc[o]] - sz[lc[last]];
            return (need - Sum) / (R - L + 1 - cnt);
        }
        int mid = (l + r) >> 1;
        double now = Sum + sumv[lc[o]] - sumv[lc[last]] + 1ll * mid * (R - L + 1 - (cnt + sz[lc[o]] - sz[lc[last]]));
        if(now >= need) return query(lc[o], lc[last], l, mid, need, cnt, Sum);
        else return query(rc[o], rc[last], mid + 1, r, need, cnt + sz[lc[o]] - sz[lc[last]], Sum + sumv[lc[o]] - sumv[lc[last]]);
    }
    int main() {
        //ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> q;
        for(int i = 1; i <= n; i++) cin >> h[i], sum[i] = sum[i - 1] + h[i];
        build(rt[0], 1, 100000);
        for(int i = 1; i <= n; i++) insert(rt[i], rt[i - 1], 1, 100000, h[i]);
        while(q--) {
            double x, y;
            cin >> L >> R >> x >> y;
            double now = (1 - x / y) * (sum[R] - sum[L - 1]);
            printf("%.10f
    ", query(rt[R], rt[L - 1], 1, 100000, now, 0, 0));
        }
        return 0;
    }
    
    

    I.KM and M

    考虑按位贡献。
    那么对于每个二进制位,假设为(2^B),其对答案的贡献为(sum_{k=0}^n(km>>B&1)cdot2^B)
    对于每个二进制位,我们可以用取模的方法得出,上式即为:(sum_{k=0}^nkm\%2^{B+1}-km\%2^B)
    现在考虑对(sum_{k=0}^{n} km\%2^B)求解,式子可变换为(sum_{k=0}^{n}km-(km>>B<<B)),表示把二进制高位减去。
    之后进一步化简:(frac{mk(k+1)}{2}-2^Bsum_{k=0}^{n}frac{km}{2^B})
    后面部分类欧即可解决。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef __int128 ll;
    const int MOD = 1000000007, inv2 = (MOD + 1) / 2;
    ll f(ll n, ll a, ll b, ll c) {
        if(a <= 0) return 0;
        if(a >= c || b >= c) {
            return (n * (n + 1) % MOD * inv2 % MOD * (a / c) % MOD
            + (n + 1) * (b / c) % MOD + f(n, a % c, b % c, c)) % MOD;
        }
        ll m = (a * n + b) / c;
        return (m * n % MOD - f(m - 1, c, c - b - 1, a) + MOD) % MOD;
    }
    ll calc(ll n, ll m, ll B) {
        ll ans = m * n % MOD * (n + 1) % MOD * inv2 % MOD;
        ll tmp = (1ll << B);
        return ((ans - tmp * f(n, m, 0, tmp) % MOD) + MOD) % MOD;
    }
    long long n, m;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        ll ans = 0;
        for(int i = 0; i <= 40; i++) if(m >> i & 1) {
            ll ans1 = calc(n, m, i);
            ll ans2 = calc(n, m, i + 1);
            ans = (ans + (ans2 - ans1 + MOD) % MOD) % MOD;
        }
        cout << (long long)ans;
        return 0;
    }
    
    

    J.Symmetrical Painting

    首先判断出答案一定位于某处的中线,否则假如答案位于两条中线之间,那么往两边走不会丢失最优解。
    那么最直接的思路就是枚举中线计算答案。
    但怎么计算面积?
    注意到对面积有贡献的线为矩形的上下或中线,那么我们将所有矩形的这三条线拿出来排序。之后从低到高扫描,统计当前位于多少个矩形下半部及上半部,根据这个计算答案即可。
    可以维护一个变量表示当前的累加值。
    具体见代码:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef double db;
    const int MAXN = 3e5 + 5, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
    const ll inf = 1000000000000000010LL, INFL = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-9;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define random(a,b) ((a)+rand()%((b)-(a)+1))
    int n, l[MAXN], r[MAXN], m;
    inline int sign(db x) {
        if (fabs(x) < eps)return 0;
        return x > eps ? 1 : -1;
    }
    struct node {
        int type;
        db y;
        bool operator <(const node &rhs)const {
            if (sign(y - rhs.y) == 0)return type > rhs.type;
            return y < rhs.y;
        }
    }a[MAXN * 3];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n; m = n * 3;
        for (int i = 1; i <= n; i++) {
            cin >> l[i] >> r[i];
            a[1 + (i - 1) * 3] = { 0, (db)l[i] };
            a[2 + (i - 1) * 3] = { 1, (l[i] + r[i]) / 2.0 };
            a[3 + (i - 1) * 3] = { 2 ,(db)r[i] };
        }
        sort(a + 1, a + 1 + m);
        int cnt1 = 0, cnt2 = 0;
        db ans = 0, sum = 0;
        for (int i = 1; i <= m; i++) {
            if (i > 1) {
                db d = a[i].y - a[i - 1].y;
                sum += cnt1 * d - cnt2 * d;
            }
            if (a[i].type == 0) cnt1++;
            else if (a[i].type == 1)cnt1--, cnt2++;
            else cnt2--;
     
            if (cnt1 < cnt2) {
                ans = max(ans, 2 * sum);
            }
            else {
                db d = a[i + 1].y - a[i].y;
                ans = max(ans, 2 * (sum + cnt1 * d - cnt2 * d));
            }
        }
        cout << (ll)(ans + eps) << '
    ';
        return 0;
    }
    
  • 相关阅读:
    可视化工具 kibana 的安装和使用
    常见的数据类型
    Elastic Search 分词器的介绍和使用
    基于 TCP 协议的网络编程
    Java7 的 NIO.2
    NIO(New IO)
    Java9 改进的对象序列化
    反射和泛型
    使用反射生成 JDK 动态代理
    使用反射生成并操作对象
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11367550.html
Copyright © 2020-2023  润新知