给出一个长度为n的序列a[]
给出q组询问,每组询问形如<x,y>,求a序列的全部区间中,数字x的出现次数与数字y的出现次数相同的区间有多少个c++
这道题是一个优雅的暴力题,咱们先处理掉特殊状况:x,y 中至少一者未出如今序列 a 中。 而后考虑怎么计算询问,一个显然正确的暴力是维护一个前缀值,扫一遍,若是遇到x就加一,遇到y就减一,而后看前面有多少个前缀值跟当前的值相同, 加进答案里。这样作的缺点在于每次都要从新 O(n)地扫一遍,可是其中有不少 位置是没有用的,因此咱们能够把x的全部出现位置和y的全部出现位置拿出来, 排序以后从前日后扫,作法与前面的暴力相似,可是压缩了中间没有影响的位置。 因为这样作至关于将全部颜色不一样的位置对都枚举了一遍,因此这样的复杂度是 O(n^2)的spa
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int i,j,n,m,k,l,a[80001],hash[8000001],bz[8000001],last[8000001],ans,x,y,num,num1,t[16001],wz,wz1; struct nup{int next,to;}f[80001]; int add_hash(long long x,int z) { long long y=x%7000007; while(hash[y]!=0&&hash[y]!=x) y++,y%=7000007; if(z==2) { if(hash[y]==0) hash[y]=x;return y; } else if(hash[y]==0) return 0;else return y; } int main() { freopen("query.in","r",stdin); freopen("query.out","w",stdout); scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i]); k=add_hash(a[i],2); if(bz[k]==0) bz[k]=i,f[i].to=-1,last[k]=i; else f[i].to=bz[k],f[bz[k]].next=i,bz[k]=i,last[k]=i; } for(i=1;i<=n;i++) if(f[i].next==0) f[i].next=n+1; for(i=1;i<=m;i++) { if(i==43) k=0; scanf("%d%d",&x,&y);ans=0; wz=add_hash(x,1);wz1=add_hash(y,1); wz=last[wz];wz1=last[wz1]; if(wz==0||wz1==0) { if(wz==0&&wz1==0) ans=(1+n)*n/2; else if(wz==0) { num=wz1;num1=n; while(num!=-1) { ans+=(1+(num1-num))*(num1-num)/2; num1=num-1;num=f[num].to; } num=0;ans+=(1+(num1-num))*(num1-num)/2; } else { num=wz;num1=n; while(num!=-1) { ans+=(1+(num1-num))*(num1-num)/2; num1=num-1;num=f[num].to; } num=0;ans+=(1+(num1-num))*(num1-num)/2; } } else { memset(t,0,sizeof t); ans+=(1+n-max(wz,wz1))*(n-max(wz,wz1))/2; t[0]++; t[0]+=n-max(wz,wz1);int cnt=0; while(wz!=-1||wz1!=-1) { if(wz>wz1) { cnt++; int kk=f[wz].to;if(kk==-1) kk=0; int ll=wz1;if(wz1==-1) wz1=0; if(kk>wz1) { if(cnt>=0) ans+=(t[cnt]+t[cnt]+wz-kk-1)*(wz-kk)/2,t[cnt]+=wz-kk; else ans+=(t[cnt*-1+8001]+t[cnt*-1+8001]+wz-kk-1)*(wz-kk)/2,t[cnt*-1+8001]+=wz-kk; } else { if(cnt>=0) ans+=(t[cnt]+t[cnt]+wz-wz1-1)*(wz-wz1)/2,t[cnt]+=wz-wz1; else ans+=(t[cnt*-1+8001]+t[cnt*-1+8001]+wz-wz1-1)*(wz-wz1)/2,t[cnt*-1+8001]+=wz-wz1; } wz=f[wz].to;wz1=ll; } else { cnt--; int kk=f[wz1].to;if(kk==-1) kk=0; int ll=wz;if(wz==-1) wz=0; if(kk>wz) { if(cnt>=0) ans+=(t[cnt]+t[cnt]+wz1-kk-1)*(wz1-kk)/2,t[cnt]+=wz1-kk; else ans+=(t[cnt*-1+8001]+t[cnt*-1+8001]+wz1-kk-1)*(wz1-kk)/2,t[cnt*-1+8001]+=wz1-kk; } else { if(cnt>=0) ans+=(t[cnt]+t[cnt]+wz1-wz-1)*(wz1-wz)/2,t[cnt]+=wz1-wz; else ans+=(t[cnt*-1+8001]+t[cnt*-1+8001]+wz1-wz-1)*(wz1-wz)/2,t[cnt*-1+8001]+=wz1-wz; } wz1=f[wz1].to;wz=ll; } } } printf("%d\n",ans); } }
有两个长度为n的排列A和B,定义排列的价值f(A,B)为全部知足A[i]>B[i]的位置i的数量。
现给出n,A,B和S,其中A和B中有一些位置的数未知,问有多少种可能的填数的方案使得f(A,B)=Scode
对于这样一道题咱们很显然能够将其拆成两个子问题来处理,一个是A为0,B不为0 || A不为0,B为0;而后提早减去二者都不为0的状况这对后面操做没有影响。最后再把两个子问题合并就好了
咱们这里考虑填A的:
假设咱们先设 DP方程:f[i][j]:作完前i个有j个A>B,转移显然,但咱们会发现若是我知足了有j个A>B的方案 然后面的(i-j)个A要比B小,会存在一种状况使得(i-j)个A并不能彻底小于B,这就会出现 有>j个A>B的状况。 因此咱们把 f[i][j]的定义改成:作完前i个至少有j个A>B。
转移: f[i][j]=f[i-1][j]+f[i-1][j-1]*(d[i]-j),d[i]表示比A的第i个数小的B的个数,(剩下的d[i]-j个数中随机选一个的方案数);
因为咱们只规定了必须有贡献的j个,剩下的随意分配,因此要f[n][i]*(n-i)!
但这样算的结果不是刚好贡献为j,而是至少贡献为j的方案,咱们下面称为f[j]。咱们设g[j]表示贡献刚好为j的方案。咱们对f[y]的定义仔细研究后发现一个g[x]在f[y] (x>y)中计算了\(C_x^y\)次(由于你会在x个贡献中任意选取y个做为必须贡献)咱们发现g[n]=f[n]的,而后咱们倒推就好了,\(g[y]=f[y]-\sum_{x=y+1}^{n}g[x]*C_x^y\),最后将两个子问题合并便可。
代码太丑仅供参考:blog
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; ll ans,x,j,n,m,k,l,dq[40001],dq1[40001],t1[40001],t[40001],g[4001],h[4001],f[4001][4001],f1[4001][4001],d[40001],d1[40001],dl[40001],dl1[40001],mo,js[40001],a[4001],b[4001]; ll ksm(ll x,ll y) { ll res=1; while(y) { if(y%2==1) res*=x,res%=mo; x*=x;x%=mo;y/=2; } return res; } inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } ll C(ll x,ll y) { ll res=js[x]*ksm(js[x-y]*js[y]%mo,mo-2);res%=mo;return res; } int main() { freopen("arrange.in","r",stdin); freopen("arrange.out","w",stdout); n=read();k=read();mo=1000000007; js[1]=1;js[0]=1;for(register int i=2;i<=n;++i) js[i]=js[i-1]*i,js[i]%=mo; for(register int i=1;i<=n;++i) a[i]=read(),t[a[i]]++; for(register int i=1;i<=n;++i) if(t[i]==0) dl[++dl[0]]=i; for(register int i=1;i<=n;++i) b[i]=read(),t1[b[i]]++; for(register int i=1;i<=n;++i) if(t1[i]==0) dl1[++dl1[0]]=i; for(register int i=1;i<=n;++i) { if(a[i]!=0&&b[i]!=0) {if(a[i]>b[i])k--;} else{if(a[i]==0) dq[++dq[0]]=b[i];else dq1[++dq1[0]]=a[i];} } sort(dq+1,dq+1+dq[0]);sort(dq1+1,dq1+1+dq1[0]); j=0; for(register int i=1;i<=n;++i)if (!t[i]){ while(i>dq[j+1]&&j<dq[0]) ++j; d[++d[0]]=j; } j=0; for(register int i=1;i<=dq1[0];++i){ while (dq1[i]>dl1[j+1]&&j<dl1[0])++j; d1[i]=j; } f[0][0]=1; for(register int i=1;i<=dq[0];++i) for(j=0;j<=i;++j) { if(j<i) f[i][j]+=f[i-1][j],f[i][j]%=mo; if(j!=0&&d[i]>=j) f[i][j]+=f[i-1][j-1]*(d[i]-j+1)%mo,f[i][j]%=mo; } for(register int i=dq[0];i>=0;i--){ g[i]=f[dq[0]][i]*js[dq[0]-i]%mo; for(j=i+1;j<=dq[0];++j) g[i]=(g[i]-C(j,i)*g[j]%mo+mo)%mo; } f1[0][0]=1; for(register int i=1;i<=dq1[0];++i) for(j=0;j<=i;++j) { if(j<i) f1[i][j]+=f1[i-1][j],f1[i][j]%=mo; if(j!=0&&d1[i]>=j) f1[i][j]+=f1[i-1][j-1]*(d1[i]-j+1)%mo,f1[i][j]%=mo; } for(register int i=dq1[0];i>=0;i--){ h[i]=f1[dq1[0]][i]*js[dq1[0]-i]%mo; for(j=i+1;j<=dq1[0];++j) h[i]=(h[i]-C(j,i)*h[j]%mo+mo)%mo; } for(register int i=0;i<=k;++i) { ans+=g[i]*h[k-i];ans%=mo; } printf("%lld",ans); }
这个写的很好,至于O(N)的作法其实就是换根DP,每次换根只会改变两个 state;
代码:排序
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int i,j,n,m,k,l,f[2000001],bz[2000001],e[4000011][2],h[2000001],a[2000001],tot,ans[2000001],x,y; void add(int u,int v) { e[++tot][0]=h[u]; e[tot][1]=v; h[u]=tot; } void dfs(int x,int father) { int cnt=0; for(int i=h[x];i;i=e[i][0]) if(e[i][1]!=father) dfs(e[i][1],x),cnt++; if(cnt==0) {bz[x]=1;return;} for(int i=h[x];i;i=e[i][0]) if(e[i][1]!=father) { if(a[x]>a[e[i][1]]&&bz[e[i][1]]==1) {bz[x]=2;return;} } bz[x]=1;return; } void bfs(int x,int father) { int num=0,dq=0; for(int i=h[x];i;i=e[i][0]) if(a[x]>a[e[i][1]]&&bz[e[i][1]]==1) num++,dq=e[i][1]; for(int i=h[x];i;i=e[i][0]) if(e[i][1]!=father) { int wz=bz[x],wz1=bz[e[i][1]]; if(num>1) bz[x]=2;else if(num==0) bz[x]=1;else if(e[i][1]!=dq) bz[x]=2;else bz[x]=1; if(bz[e[i][1]]==1) if(a[e[i][1]]>a[x]&&bz[x]==1) bz[e[i][1]]=2; if(bz[e[i][1]]==2) ans[++ans[0]]=e[i][1]; bfs(e[i][1],x); bz[x]=wz;bz[e[i][1]]=wz1; } } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,0); if(bz[1]==2) ans[++ans[0]]=1; bfs(1,0); sort(ans+1,ans+1+ans[0]); for(i=1;i<=ans[0];i++) printf("%d ",ans[i]); }
完结撒花ip