Codeforces Round #404 (Div. 2) C 二分,水 D 数学,好题 E 分块

Codeforces Round #404 (Div. 2)

C. Anton and Fairy Tale

题意:仓库容量n,每天运来m粮食,第 i 天被吃 i 粮食,问第几天仓库第一次空掉。

tags:==SB题   注:二分边界判断,数据范围爆long long判断。

// CF404 C
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 200005;

ll n, m;
int main()
{
    cin>>n>>m;
    if(n<=m) return 0*printf("%lld
", n);
    ll l=m+1, r=2e18, ans;        //边界 
    while(l<=r) {
        ll mid=l+((r-l)>>1);
        if(mid-m>2e9) {            //判断爆long long 
            r=mid-1, ans=mid;
            continue;
        }
        ll s1=n-m, s2=((mid-m)*(1+mid-m))>>1;
        if(s1<=s2) r=mid-1, ans=mid;
        else l=mid+1;
    }
    printf("%lld
", ans);

    return 0;
}
View Code

D. Anton and School - 2

题意:一个字符串 s,只包含小括号'('和')'。定义长度为len的RSBS序列:1、len不为0且为偶数。2、前一半的字符都是'(',后一半的字符都是')'。  问s 有多少个子序列是RSBS序列

tags: 好题

1、首先总体思路要正确,即要想到根据1~len-1个分界线来求。所以先枚举求出每个分界线左边有 l[i]个左括号,右边有 r[i]个右括号。 

2、为了不重复,只选择左边第一个恰好是左括号的分界线计算。比如到了第 i 条分界线且分界线左边第一个恰好是左括号,则为了不重复这个左括号必须选,然后在 l[i]-1 个左括号中选 j 个,在r[i]个右括号中选 j+1 个。 

3、 ans= , 因为i<=l[i]-1,且i+1<=r[i],所以可取k=r[i]-1。由范德蒙恒等式则ans=

4、逆元求组合数。

// CF404  D
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 200005, mod=1000000007;

char s[N];
int l[N], r[N], len;
ll sum, fac[N], inv[N];
ll fpow(ll a, int b) {ll ans=1; for(;b;a=a*a%mod,b>>=1)if(b&1)ans=ans*a%mod; return ans;}
ll Inv(ll x) {return fpow(x, mod-2);}
ll C(int a, int b) 
{ 
    if(b==0 || a==b) return 1;
    return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
int main()
{
    scanf("%s", s+1);
    len=strlen(s+1);
    rep(i,1,len-1) l[i]=l[i-1]+ (s[i]=='('?1:0);
    per(i,len-1,1) r[i]=r[i+1]+ (s[i+1]==')'?1:0);
    
    fac[0]=1;
    rep(i,1,len) fac[i]=fac[i-1]*i%mod, inv[i]=Inv(fac[i])%mod;        //求组合数预处理优化,inv[i]表示i的阶乘的逆元 
    
    rep(i,1,len-1) if(s[i]=='(') {
        sum=(sum+C(l[i]+r[i]-1, r[i]-1))%mod;    
    }
    printf("%lld
", (sum+mod)%mod);

    return 0;
}
View Code

E. Anton and Permutatio

题意:n个数的序列a[],有q个询问。每次询问有 l、r,交换a[l]和a[r]的值,求[l, r]区间的逆序数对数。

tags:第一次搞分块,好暴力,,看了题解强行码出来的==

对于区间问题,且不好用线段树或树状数组的情况,可以考虑分块做。

主要思路是:交换a[l]、a[r]后, 区间[0,l-1]和[r+1,n-1]的不用考虑。而[l, r]的,看有多少>a[l]或<a[r]的,和有多少<a[l]或>a[r]的,更新ans。     这题,分好块后,对两边单独的两块直接扫;对于中间的每一块,二分解决。    在更新块时,也同样是二分来实现块中有序插入和删除a[l]、a[r] 。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define ALL(x) x.begin(),x.end()
typedef long long ll;
const int N = 200005, M=500;

int n, q, a[N];
int st[M], ed[M], l, r, idl, idr, col;
ll ans;
vector<int >b[M];
void Init()
{
    scanf("%d %d", &n, &q);
    //分块 
    col=(int)sqrt(n);  //块大小 
    rep(i,0,n/col-1) st[i]=col*i, ed[i]=col*(i+1);     //标号是从 0开始的, 注意
    if(n%col) st[n/col]=n-(n%col), ed[n/col]=n;
    
    rep(i,0,n-1) {
        a[i]=i;
        b[i/col].push_back(i);
    }
}
void update()
{
    if(idl<idr) {    // 二分更新数据 ,实现 vector的有序插入、删除 
        b[idl].erase(lower_bound(ALL(b[idl]), a[l]));    
        b[idl].insert(lower_bound(ALL(b[idl]), a[r]), a[r]);
        b[idr].erase(lower_bound(ALL(b[idr]), a[r]));
        b[idr].insert(lower_bound(ALL(b[idr]), a[l]), a[l]);
    }
    if(a[l]<a[r]) ans++; else ans--;
    swap(a[l], a[r]);
}
void solve()
{
    scanf("%d %d", &l, &r);
    l--, r--;
    if(l==r) { printf("%lld
", ans); return ;}
    if(l>r) swap(l, r);
    idl=l/col, idr=r/col;
    //对中间块每一块二分 
    rep(i,idl+1,idr-1) {
        int Size=b[i].size();
        int p=lower_bound(ALL(b[i]), a[l])-b[i].begin();
        ans-=p, ans+= Size-p;
        p=lower_bound(ALL(b[i]), a[r])-b[i].begin();
        ans+=p, ans-= Size-p;
    } 
    //对两边的单独两块,直接暴扫 
    for(int i=l+1; i<ed[idl] && i<r; i++) {
        if(a[i]>a[l]) ans++; else ans--;
        if(a[i]>a[r]) ans--; else ans++;
    }
    for(int i=st[idr]; i<r && l<i; i++) {
        if(a[i]>a[l]) ans++; else ans--;
        if(a[i]>a[r]) ans--; else ans++;
    }
    
    update();
    printf("%lld
", ans);
}
int main()
{
    Init();
    while(q--) {
        solve();
    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/sbfhy/p/6575246.html