多校第五场(字符串)
5783.Divide the Sequence
题意:给出一段序列,尽可能多得分割序列使得每段前缀和非负。
分析:既然前缀和非负,那么从后往前遍历,若非负则++。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <math.h> #include <iostream> #include <algorithm> #include <string> #include <set> #include <map> #include <queue> #include <stack> #include <vector> #include <bitset> using namespace std; #define LL long long #define F(i,a,b) for (int i=(a),_##i=(b); i<=_##i; i++) #define Rof(i,a,b) for (int i=(a),_##i=(b); i>=_##i; i--) #define rep(i,a,b) for (int i=(a),_##i=(b); i<=_##i; i++) #define rek(i,a,b) for (int i=(a),_##i=(b); i>=_##i; i--) #define mem(a,b) memset(a,b,sizeof(a)) #define Cpy(a,b) memcpy(a,b,sizeof(b)) int a[1001000],n; long long sum,m; int main() { //freopen("in.txt","r",stdin); while(scanf("%d",&n)==1) { sum=m=0; F(i,1,n) scanf("%d",a+i); for(int i=n;i;--i) { m+=a[i]; if(m>=0) { sum++;m=0; } } printf("%I64d\n",sum); } return 0; }
5791.Two
题意:给出两个数字序列,求两个序列的公共子序列的个数(可重复)
分析:可dp,可用树状数组做。
先给出dp方程,再解释.
dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1])%mod;——1
if(dp[i][j]<0) dp[i][j]+=mod;——2
if(a[i]==b[j]) dp[i][j]=(dp[i][j]+dp[i-1][j-1]+1)%mod;——3
第一个是每次都要使用的,因为dp[i][j]要加上除去a[i]的(dp[i-1][j]),加上除去b[j]的(dp[i][j-1],再减去重复加的(dp[i-1][j-1],就ok啦。
第二个是保证dp[i][j]为正,因为在减dp[i-1][j-1]中可能为负,故要处理。
最后是第三个,因为如果a[i]==b[j],那么对前面的所有都可加上a[i],b[j],所以要加一遍dp[i-1][j-1],然后a[i]和b[j]单独匹配得到,需要+1,
举个例子:
1 2 3 4
1 2
匹配到2时,前面所有的匹配到的(1,1)都可变成(1 2,1 2)所以需要加一遍dp[i-1][j-1],ok啦。
//jing miao de dong gui #include<cstdio> #include<cstring> const int mod=1000000007; int dp[1010][1010]; int a[1010],b[1010],n,m; int main() { while(scanf("%d%d",&n,&m)==2) { memset(dp,0,sizeof(dp)); for(int i=1;i<=n;++i) scanf("%d",a+i); for(int i=1;i<=m;++i) scanf("%d",b+i); for(int i=1;i<=n;++i)for(int j=1;j<=m;++j) { dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1])%mod; if(dp[i][j]<0) dp[i][j]+=mod; if(a[i]==b[j]) dp[i][j]=(dp[i][j]+dp[i-1][j-1]+1)%mod; } printf("%d\n",dp[n][m]); } }
5792.World is Exploding
题意:给出数字序列,找出满足某些条件的四个数的组数,Aa<Ab&&a<b,Ac>Ad&&c<d.
分析:因为数字范围很大(达到1e9),而个数最多只有5万个,故要离散化。然后对每个数统计左边大于(l[])和小于(l1[])它的数,右边大于(r[])和小于(r1[])它的数。
最后处理特殊情况,左边*右边-特殊情况就可以得到结果了,具体见BC的blog。
#include<bits/stdc++.h> using namespace std; const int maxn = 5e4+7; int d[maxn],n,a[maxn],l[maxn],r[maxn],l1[maxn],r1[maxn]; inline int lowbit(int x){ return x&(-x); } //改点求段 inline void update(int x,int v){ for(int i=x;i<maxn;i+=lowbit(i)) d[i]+=v; } //d[1]~d[x] inline int get(int x){ int ans = 0; for(int i=x;i;i-=lowbit(i)) ans+=d[i]; return ans; } map<int,int>H; vector<int> V; int main(){ while(scanf("%d",&n)!=EOF){ fill(l1,l1+maxn-1,0); //memset(l1,0,sizeof(l1)); memset(r1,0,sizeof(r1)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); memset(d,0,sizeof(d)); V.clear();H.clear(); for(int i=1;i<=n;i++) { scanf("%d",a+i); V.push_back(a[i]); } sort(V.begin(),V.end());//排序 V.erase(unique(V.begin(),V.end()),V.end());//去重+翻转数列 //for(int i=0;i<n;i++) printf("%d ",V[i]); for(int i=0;i<V.size();i++)//hash? H[V[i]]=i+1; for(int i=1;i<=n;i++)a[i]=H[a[i]]; long long ans1=0,ans2=0,ans3=0;//树状数组求左边大于或小于它的数 for(int i=1;i<=n;i++){ l[i]=get(a[i]-1); l1[i]=get(maxn-1)-get(a[i]); update(a[i],1); } memset(d,0,sizeof(d)); for(int i=n;i>=1;i--){//更新加计算 右边大于或小于它的数 r[i]=get(a[i]-1); r1[i]=get(maxn-1)-get(a[i]); update(a[i],1); } for(int i=1;i<=n;i++) printf("l[i]=%d l1[i]=%d r[i]=%d rl[i]=%d\n",l[i],l1[i],r[i],r1[i]); for(int i=1;i<=n;i++){//统计 ans3+=1ll*l[i]*r[i]; ans3+=1ll*l1[i]*r1[i]; ans3+=1ll*l[i]*l1[i]; ans3+=1ll*r[i]*r1[i]; ans1+=l[i],ans2+=r[i]; } cout<<ans1*ans2-ans3<<endl;//小的*大的-特殊情况 } }