Codeforces Round #404 (Div. 2) C 二分,水 D 数学,好题 E 分块
Codeforces Round #404 (Div. 2)
题意:仓库容量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", 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\n", ans); return 0; }
题意:一个字符串 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\n", (sum+mod)%mod); return 0; }
题意: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\n", 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\n", ans); } int main() { Init(); while(q--) { solve(); } return 0; }