給出一個長度爲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