Educational Codeforces Round 65

Educational Codeforces Round 65
E:
f ( l , r ) f(l,r) f(l,r) 的定义为从 a a a 中删除所有 l ≤ a i ≤ r l \leq a_i \leq r lair 得到的数列,题意是要求出有多少对 ( l , r ) (l,r) (l,r) 满足 f ( l , r ) f(l,r) f(l,r) 非递减。
G ( p ) G(p) G(p) 为删除 a i &lt; p a_i &lt; p ai<p 所有数后得到的数列。设 L ( p ) L(p) L(p) 为删除所有 a i &gt; p a_i&gt;p ai>p 的所有数后得到的数列。可以看出 f ( l , r ) = G ( l ) ∪ L ( r ) f(l,r)=G(l) \cup L(r) f(l,r)=G(l)L(r) 。对于每一个数组可以发现有一个固定的 p ′ p&#x27; p,当 p &gt; p ′ p&gt;p&#x27; p>p 时, G ( p ) G(p) G(p) 才非递减,因此可以通过枚举非递减 G ( p ) G(p) G(p),对每一个 p p p 枚举一个最大的 k k k,使得 F ( k ) F(k) F(k) 插入到 G ( p ) G(p) G(p) 后整个数列非递减。将所有结果求和即为答案。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
vector<int> num[N];
int a[N];
int main() {
    int n, x;
    scanf("%d%d", &n, &x);
    for(int i=1; i<=n; ++i) {
        int k;
        scanf("%d", &k);
        num[k].push_back(i);
    }
    int nl, nr;
    int mx=0;
    memset(a, 0, sizeof(a));
    for(nl=1; nl<=x;++nl) {
        if(num[nl].size()) {
            bool ok = true;
            int tmp = mx;
            for(int k : num[nl]) {
                if(k<mx) ok = false;
                tmp = max(tmp, k);
            }
            if(!ok) break;
            for(int k : num[nl]) {
                a[k] = nl;
            }
            mx = tmp;
        }
    }
    --nl;
    int mn=n+1;
    for(nr=x;nr>=1;--nr) {
        if(num[nr].size()) {
            bool ok = true;
            int tmp = mn;
            for(int k : num[nr]) {
                if(k>mn) ok = false;
                tmp = min(tmp, k);
            }
            if(!ok) break;
            mn = tmp;
        }
    }
    ++nr;
//    cout << nl << " " << nr << endl;
    int l = min(nl,x-1); // 1 2 3 4 5
    int p = n;
    long long ans = l+1;
    for(int i=x;i>=nr;--i) {
        if(num[i].size()) { // i表示选取[i,x]有序序列,截掉的最大串应该为[1, i-1]
            int mn = N;
            for(int k : num[i]) {
                mn=min(mn, k);
            }
            for(int i=mn; i<=p; ++i) {
                if(a[i]) l = min(l, a[i]-1); //删掉一些冲突的。
            }
            p=mn-1;
        }
        ans += min(l+1, i-1);
    }
    printf("%I64d\n", ans);
}

F:
通过观察可以发现 f ( l , r ) = ∑ i = l r a i × ( l o w e r ( l , r , a i ) + 1 ) f(l,r)=\sum_{i=l}^{r}a_i \times (lower(l, r,a_i)+1) f(l,r)=i=lrai×(lower(l,r,ai)+1)
其中 l o w e r ( l , r , x ) lower(l,r,x) lower(l,r,x) 为在区间 [ l , r ] [l,r] [l,r]内小于 x x x 的数的数量。然后经过一阵推导可以将问题转化为枚举所有 a i a_i ai ,对lower求和的问题,对原问题进行了简化。解决这个问题的关键是能想到把枚举 a i a_i ai 的求和式提到最外面来,从而转化为枚举 a i a_i ai 计算的问题,简化了复杂度。
具体推导

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
using ll = long long;
const ll mod = 1e9+7;
int n;
ll c[N];
ll L[N], R[N];
int a[N];
int b[N], rk[N];
int lowbit(int x) {
    return x&-x;
}
void update(int x, ll v) {
    for(; x<=n; x+=lowbit(x)) c[x]+=v;
}
ll query(int x) {
    ll res = 0;
    for(; x>0; x-=lowbit(x)) res+=c[x];
    return res%mod;
}
int main() {
    scanf("%d", &n);
    for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
    for(int i=1; i<=n; ++i) b[i]=i;
    sort(b+1, b+1+n, [](int l, int r){ return a[l]<a[r]; });
    for(int i=1; i<=n; ++i) rk[b[i]]=i;
    for(int i=1; i<=n; ++i) {
        L[i] = query(rk[i]);
//        cout << L[i] << endl;
        update(rk[i], i);
    }
    memset(c, 0, sizeof(c));
    for(int i=n; i>=1; --i) {
        R[i] = query(rk[i]);
        update(rk[i], n-i+1);
    }
//    for(int i=1; i<=n; ++i) {
//        printf("lr: %I64d %I64d\n", L[i], R[i]);
//    }
    ll ans = 0;
    for(int i=1; i<=n; ++i) {
        ans += 1LL*a[i]*(((n-i+1)*L[i]%mod+i*R[i]%mod)%mod)%mod;
        ans %= mod;
        ans += 1LL*a[i]*(n-i+1)%mod*i%mod;
        ans%=mod;
//        cout << ans << endl;
    }
    printf("%I64d\n", ans);
}

G:
将碰撞看成两类:块与块的碰撞和块与线的碰撞,从而能进行分类讨论。
对于块与块的碰撞,可以观察到当且仅当两个块距离对折点的距离小于等于1时才会碰撞,且离对折点最近的块与块碰撞最先发生。因此可以枚举对折点两端块分布的bitset,按位与用 _Find_first 找到第一个碰撞点。不需要维护所有碰撞点,因为题目有块之间最大距离为d的限制,可以保证仅仅枚举左右两端各长为d的bitset,可以保证得到的最小弧度是 a r c t a n 2 d arctan\frac{2}{d} arctand2 ,bitset范围之外就算碰撞,最大弧度也只有 a r c t a n 2 d arctan \frac{2}{d} arctand2
对于线与块的碰撞,分别找左右两边第一个块就行。

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
using ll = long long;
const ll mod = 1e9+7;
int n;
ll c[N];
ll L[N], R[N];
int a[N];
int b[N], rk[N];
int lowbit(int x) {
    return x&-x;
}
void update(int x, ll v) {
    for(; x<=n; x+=lowbit(x)) c[x]+=v;
}
ll query(int x) {
    ll res = 0;
    for(; x>0; x-=lowbit(x)) res+=c[x];
    return res%mod;
}
int main() {
    scanf("%d", &n);
    for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
    for(int i=1; i<=n; ++i) b[i]=i;
    sort(b+1, b+1+n, [](int l, int r){ return a[l]<a[r]; });
    for(int i=1; i<=n; ++i) rk[b[i]]=i;
    for(int i=1; i<=n; ++i) {
        L[i] = query(rk[i]);
//        cout << L[i] << endl;
        update(rk[i], i);
    }
    memset(c, 0, sizeof(c));
    for(int i=n; i>=1; --i) {
        R[i] = query(rk[i]);
        update(rk[i], n-i+1);
    }
//    for(int i=1; i<=n; ++i) {
//        printf("lr: %I64d %I64d\n", L[i], R[i]);
//    }
    ll ans = 0;
    for(int i=1; i<=n; ++i) {
        ans += 1LL*a[i]*(((n-i+1)*L[i]%mod+i*R[i]%mod)%mod)%mod;
        ans %= mod;
        ans += 1LL*a[i]*(n-i+1)%mod*i%mod;
        ans%=mod;
//        cout << ans << endl;
    }
    printf("%I64d\n", ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值