2019杭电多校第二场

今天也是一条咸鱼ORZ
场上三题:E,J,K

E:Everything Is Generated In Equal Probability

题解:一个长度为n的排列的逆序期望为C(n, 2)/2 ,因为每一对下标的贡献都是1/2.然后设 d p [ i ] dp[i] dp[i]为长度为 i i i的随机排列的culculate函数值,则由题可得
d p [ n ] = C ( n , 2 ) 2 + Σ i = 0 n C ( n , i ) ∗ d p [ i ] dp[n]=\frac{C(n,2)}{2}+\Sigma_{i=0}^nC(n,i)*dp[i] dp[n]=2C(n,2)+Σi=0nC(n,i)dp[i]
由此可以 O ( n 2 ) O(n^2) O(n2)推出 d p [ n ] dp[n] dp[n]。因为一开始从 [ 1 , n ] [1,n] [1,n]随机选取长度,所以最终答案为 Σ d p [ i ] n \frac{\Sigma dp[i]}{n} nΣdp[i]。(听说有大佬 O ( 1 ) O(1) O(1)直接算出答案,%%%)
代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 998244353;
const int maxn = 3e3 + 50;
ll qm(ll a, ll b)
{
    ll res = 1;
    while(b){
        if(b&1) res = res*a%mod;
        a = a*a%mod;
        b >>= 1;
    }return res;
}
ll dp[maxn];
ll p[maxn];
ll inv[maxn];
ll bin[maxn];
ll sum[maxn];
int main()
{
	dp[0] = dp[1] = 0;
	bin[0] = 1;
	p[0] = inv[0] = 1;
	for(int i = 1; i < maxn; ++i) p[i] = p[i-1]*i%mod, inv[i] = qm(p[i], mod-2), bin[i] = bin[i-1]*2%mod;
	for(int i = 2; i < maxn; ++i){
        ll res = bin[i-2]*i%mod*(i-1)%mod;
        for(int j = 2; j < i; ++j){
            res = (res + p[i]*inv[j]%mod*inv[i-j]%mod*dp[j])%mod;
        }
        res = res*qm(bin[i]-1, mod-2) % mod;
        dp[i] = res;
	}
	sum[0] = 0;
	for(int i = 1; i < maxn; ++i) sum[i] = (sum[i-1] + dp[i])%mod;

	int n;
	while(scanf("%d", &n)!=EOF){
        ll ans = sum[n] * qm(n, mod-2) % mod;
        ans = (ans + mod)%mod;
        printf("%lld\n", ans);
	}
}

J: Just Skip The Problem

题解:最好的选择是每一位都问过去,所以答案是n!,因为对1e6+3取模,所以当 n &gt; = 1 e 6 + 3 n&gt;=1e6+3 n>=1e6+3的时候答案为0,递推求阶乘即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e6 + 3;
ll p[mod];
int main()
{
	p[0] = 1;
	for(ll i = 1; i < mod; ++i) p[i] = p[i-1]*i%mod;
	ll n;
	while(~scanf("%lld", &n)){
        if(n >= mod) printf("0\n");
        else printf("%lld\n",p[n]);
	}
}
K:Keen On Everything But Triangle

题解:对于一个数组,从最大值开始判断是否可以组成三角形。它的最大值n如果不能组成三角形,意味着数组内值的范围为 [ n 2 , n ] [\frac{n}{2},n] [2n,n]除了它本身之外最多只有一个,所以每检查两个数,当前数值一定减少了超过一半。离散化之后用主席树查询区间比pos小的最大的数来找第k大值。(有点绕,实际上只需要查询区间第k大,代码写丑了。)
代码:

#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
using namespace std;
const int maxn = 1e5 + 50;
int sz[maxn*20], lc[maxn*20], rc[maxn*20];
int T[maxn];
int n, m;
ll cc[maxn];
int num;
ll a[maxn];
int tot;
void build(int pre, int &cur, int l, int r, int pos){
    if(!cur) {
        cur = ++tot;
        sz[cur] = lc[cur] = rc[cur] = 0;
    }
    sz[cur] = sz[pre] + 1;
    if(l == r) return;
    if(pos <= mid){
        rc[cur] = rc[pre];
        build(lc[pre], lc[cur], l, mid, pos);
    }
    else{
        lc[cur] = lc[pre];
        build(rc[pre], rc[cur], mid+1, r, pos);
    }
    return;
}
int query(int pre, int cur, int l, int r, int pos, int &tt){//小于pos的数中找最大的,tt为sz
    if(pos < l) return -1;
    if(sz[cur] - sz[pre] == 0) return -1;//区间内没有数字了
    if(l == r) {
        tt = sz[cur] - sz[pre];
        return l;
    }
    int ans = -1;
    if(pos > mid) ans = query(rc[pre], rc[cur], mid+1, r, pos, tt);
    if(ans == -1) return query(lc[pre], lc[cur], l, mid, pos, tt);
}
void init()
{
    tot = num = 0;
    for(int i = 1; i <= n; ++i){
        scanf("%lld\n", &a[i]);
        cc[++num] = a[i];
        T[i] = 0;
    }
    sort(cc+1, cc+1+num);
    num = unique(cc+1, cc+1+num) - cc - 1;
    for(int i = 1; i <= n; ++i) {
        int pos = lower_bound(cc+1, cc+1+num, a[i]) - cc;
        build(T[i-1], T[i], 1, num, pos);
    }
}
void sol()
{
    while(m--){
        int l, r;
        scanf("%d%d", &l, &r);
        int p1, p2, p3;
        int t1 = 0, t2 = 0, t3 = 0;
        p1 = query(T[l-1], T[r], 1, num, num, t1);
        p2 = query(T[l-1], T[r], 1, num, p1-1, t2);
        p3 = query(T[l-1], T[r], 1, num, p2-1, t3);
        int ok = 0;
        while(t1 + t2 + t3 >= 3){
            //cout<<"p1:"<<p1<<" p2:"<<p2<<" p3:"<<p3<<endl;
            if(t1 >= 3){
                printf("%lld\n", cc[p1]*3);ok = 1;break;
            }
            else if(t1 == 2){
                printf("%lld\n", 2*cc[p1] + cc[p2]);ok = 1;break;
            }
            else {//t1 == 1
                if(t2 >= 2 && cc[p2]*2 > cc[p1]){//找到答案
                    printf("%lld\n", 2*cc[p2] + cc[p1]); ok = 1;break;
                }
                else if(t2 >= 2){//有两个以上但是无法构成
                    p1 = p2; p2 = p3;
                    t1 = t2; t2 = t3;
                    t3 = 0;
                    p3 = query(T[l-1], T[r], 1, num, p2-1, t3);
                    continue;
                }
                else if(t3 > 0){//有第三小
                    if(cc[p2] + cc[p3] > cc[p1]){
                        printf("%lld\n", cc[p1] + cc[p2] + cc[p3]);ok = 1;break;
                    }
                    p1 = p2; p2 = p3;
                    t1 = t2; t2 = t3;
                    t3 = 0;
                    p3 = query(T[l-1], T[r], 1, num, p2-1, t3);
                    continue;
                }
            }
        }
        if(!ok) printf("-1\n");
    }
}
int main()
{
	while(scanf("%d%d", &n, &m) != EOF){
        init();sol();
	}
}
/*
10 1
1 4 4 4 4 8 15 1 1 1
1 10
*/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值