2021.02.02【NOIP提升A組】模擬

5428. 【NOIP2017提升A組集訓10.27】查詢

Description:

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

5429. 【NOIP2017提升A組集訓10.27】排列

Description:

有兩個長度爲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);
}

5448. 【NOIP2017提升A組衝刺11.3】機房比教室好多了

這個寫的很好,至於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

相關文章
相關標籤/搜索