Codeforces Round #404 (Div. 2) DE

昨晚玩游戏竟然不小心错过了CF。。我是有多浪啊。 今天总算趁着下课时间补了,感觉最后两题还是挺有意思的,写个题解。 

 

D:

题目大意:

给出一个括号序列,问有多少个子序列 是k个'(' + k个')' 这样的形式。        n<=200000

 

解法:

对于每个'('的位置,计算以它为最右边的'('的合法子序列数。 假设它左边(包括它)有$l$个'(', 右边有 $r$个')' ,  如果子序列的长度2k, 那么

方案数有$\binom{l-1}{k-1} * \binom{r}{k}= \binom{l-1}{l-k} * \binom{r}{k}$

这个式子对$k$求和是  $\binom{l+r-1}{l}$  可以预处理阶乘O(1)计算出。

 

代码:

 1 #include <iostream>
 2 #include <string>
 3 #include <cstring>
 4 #include <map>
 5 #include <cmath>
 6 #include <set>
 7 #include <bitset>
 8 using namespace std;
 9 
10 typedef long long ll;
11 typedef pair<int,int> pii;
12 
13 #define N 400010
14 #define X first
15 #define Y second
16 
17 const int INF=1<<30;
18 const int Mod=1000000007;
19 int ans;
20 char s[N];
21 int fac[N],fac_inv[N];
22 
23 
24 int Power(int x,int p)
25 {
26     int res=1;
27     for (;p;p>>=1)
28     {
29         if (p&1) res=1ll*res*x%Mod;
30         x=1ll*x*x%Mod;
31     }
32     return res;
33 }
34 
35 int C(int n,int m)
36 {
37     if (m==0) return 1;
38     if (n==0) return 0;
39     int res=1ll*fac[n]*fac_inv[m]%Mod;
40     return 1ll*res*fac_inv[n-m]%Mod;
41 }
42 
43 int l[N],r[N];
44 
45 int main()
46 {
47     //freopen("in.in","r",stdin);
48     //freopen("out.out","w",stdout);
49     
50     fac[0]=fac_inv[0]=1;
51     for (int i=1;i<N;i++) 
52     {
53         fac[i]=1ll*fac[i-1]*i%Mod;
54         fac_inv[i]=Power(fac[i],Mod-2);
55     }
56     int n; scanf("%s",s+1);
57     n=strlen(s+1);
58     
59     for (int i=1;i<=n;i++) l[i]=l[i-1]+(s[i]=='(');
60     for (int i=n;i>=1;i--) r[i]=r[i+1]+(s[i]==')');
61     
62     for (int i=1;i<=n;i++)
63     {
64         if (s[i]!='(') continue;
65         if (!r[i]) continue;
66         ans+=C(l[i]+r[i]-1,l[i]);
67         if (ans>=Mod) ans-=Mod;
68     }
69     
70     printf("%d\n",ans);
71     return 0;
72 }
View Code

 


 

E:

题目大意:

一开始有一个1-n的排列,然后每次操作交换两个数,每次询问逆序对数。        n<=200000,Q<=50000

 

解法:

假设我们交换了a[l],a[r], 那么逆序对数如何改变呢?

首先逆序对数会减少[l+1,r-1]这个区间里 比a[r]大的数  和 比a[l]小的数。

然后会增加[l+1,r-1]这个区间里 比a[r]小的数  和 比a[l]大的数。

因此我们需要快速计算某个区间里有多少个数比x大的。

我的做法是分块,每个块维护一个树状数组即可。 复杂度比较悬,换了几次块的大小,极限数据本地跑要10s,但是CF机子2s就跑完了?

 

代码:

  1 By lzw4896s, contest: Codeforces Round #404 (Div. 2), problem: (E) Anton and Permutation, Accepted, #
  2  #include <iostream>
  3 #include <cstdio>
  4 #include <algorithm>
  5 #include <map>
  6 #include <set>
  7 #include <cmath>
  8 
  9 using namespace std;
 10 
 11 typedef long long ll;
 12 #define N 200010
 13 
 14 
 15 int n,m,block;
 16 int v[N],id[N];
 17 
 18 inline int Lowbit(int x){return x&-x;}
 19 
 20 struct BIT
 21 {
 22     int c[N];
 23     void Insert(int x,int v)
 24     {
 25         while (x<=n)
 26         {
 27             c[x]+=v;
 28             x+=Lowbit(x);
 29         }
 30     }
 31     int Sum(int l,int r)
 32     {
 33         if (l>r) return 0;
 34         int res=0,x=r;
 35         while (x)
 36         {
 37             res+=c[x];
 38             x-=Lowbit(x);
 39         }
 40         x=l-1;
 41         while (x)
 42         {
 43             res-=c[x];
 44             x-=Lowbit(x);
 45         }
 46         return res;
 47     }
 48 }tree[460];
 49 
 50 int Query(int l,int r,int low,int high)
 51 {
 52     int res=0;
 53     if (id[r]-id[l]<=1)
 54     {
 55         for (int i=l;i<=r;i++) res+=(v[i]>=low && v[i]<=high);
 56         return res;
 57     }
 58     for (int i=l;i<=block*id[l];i++) res+=(v[i]>=low && v[i]<=high);
 59     for (int i=block*(id[r]-1)+1;i<=r;i++) res+=(v[i]>=low && v[i]<=high);
 60     for (int i=id[l]+1;i<=id[r]-1;i++) res+=tree[i].Sum(low,high);
 61     return res;
 62 }
 63 
 64 int main()
 65 {
 66     ///freopen("in.in","r",stdin);
 67     //freopen("out.out","w",stdout);
 68     
 69     int Q,l,r; ll ans=0; 
 70     scanf("%d%d",&n,&Q);
 71     if (n>5000) block=2500;
 72     else block=500;
 73     for (int i=1;i<=n;i++)  v[i]=i;
 74     //block=4;
 75 
 76     for (int i=1;i<=n;i+=block)
 77     {
 78         int j=min(n,i+block-1); m++;
 79         for (int k=i;k<=j;k++)
 80         {
 81             id[k]=m; 
 82             tree[m].Insert(k,1);
 83         } 
 84     }
 85     while (Q--)
 86     {
 87         scanf("%d%d",&l,&r);
 88         if (l==r) 
 89         {
 90             printf("%I64d\n",ans);
 91             continue;
 92         }
 93         if (l>r) swap(l,r);
 94         if (v[l]>v[r]) ans--;
 95         else ans++;
 96         tree[id[l]].Insert(v[l],-1);
 97         tree[id[r]].Insert(v[r],-1);
 98         if (r-l>1)
 99         {
100             ans-=Query(l+1,r-1,v[r]+1,n);
101             ans-=Query(l+1,r-1,1,v[l]-1);
102             ans+=Query(l+1,r-1,1,v[r]-1);
103             ans+=Query(l+1,r-1,v[l]+1,n);
104         } 
105         swap(v[l],v[r]);
106         tree[id[l]].Insert(v[l],1);
107         tree[id[r]].Insert(v[r],1);
108         //cout<<"eee\n";
109         printf("%I64d\n",ans);
110         //for (int i=1;i<=n;i++) cout<<v[i]<<" ";
111         //cout<<endl;
112         /*for (int i=1;i<=m;i++)
113         {
114             for (int j=1;j<=n;j++) cout<<tree[i].Sum(j,j)<<" ";
115             cout<<endl;
116         }*/
117     }
118 
119     
120     return 0;
121 }
View Code

 

转载于:https://www.cnblogs.com/vb4896/p/6561040.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值