Solution of NOIP2021模拟赛 10.21 By ZJ -- zhengjun

@

A

solution

原题:[ZJOI2006]碗的叠放

显然枚举碗的叠放顺序,然后算一下高度就可以了。

高度的话,就处理出 \(i\) 放在 \(j\) 上面的增加的高度。

A_zjzj

时间复杂度:\(O(n!\times n)\)\(O(n!\times n^2)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=1e-8;
int n,h[10],a[10],b[10];
double f[10][10];
double d[10],ans=1e15;
bool vis[10];
void dfs(int now,double maxx){
	if(now>=n){
		ans=min(ans,maxx);
		return;
	}
	for(int i=1;i<=n;i++){
		if(vis[i])continue;
		for(int j=0;j<=n;j++)if(vis[j])d[i]=max(d[i],d[j]+f[j][i]);
		vis[i]=1;
		dfs(now+1,max(maxx,d[i]));
		d[i]=0;
		vis[i]=0;
	}
}
int get(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d%d%d",&h[i],&a[i],&b[i]);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(a[j]>=b[i]){f[i][j]=h[j];continue;}
			f[i][j]=h[j]-h[i];
			if(a[j]>=a[i])f[i][j]=max(f[i][j],h[j]-h[i]+double(a[j]-a[i])*h[i]/(b[i]-a[i]));
			if(b[j]>=b[i])f[i][j]=max(f[i][j],double(b[j]-b[i])*h[j]/(b[j]-a[j]));
			else if(b[j]>=a[i])f[i][j]=max(f[i][j],-double(b[i]-b[j])*h[i]/(b[i]-a[i]));
		}
	}
	for(int i=1;i<=n;i++)f[0][i]=h[i];
	vis[0]=1;dfs(0,0);
	return printf("%.0lf\n",ans),0;
}
int main(){
//	freopen("ex_A2.in","r",stdin);freopen("ex_A2.out","w",stdout);
    //if(fopen("1.in","r"))freopen("1.in","r",stdin);
	return get();
}

B

原题:[SDOI2010]地精部落

solution1: \(20\) pts

枚举排列再验证。

时间复杂度:\(O(n!\times n)\)

代码

#include<bits/stdc++.h>
using namespace std;
int n,a[4010],ans,mod;
int main(){
	cin>>n>>mod;
	for(int i=1;i<=n;i++)a[i]=i;
	do{
		bool flag=1;
		for(int i=2;i<n;i++)flag&=((a[i-1]<a[i])!=(a[i]<a[i+1]));
		ans+=flag;
	}while(next_permutation(a+1,a+1+n));
	cout<<ans%mod;
	return 0;
}

solution2:\(40\) pts

考虑状压 dp,用 \(f_{i,j}\) 表示前 \(i\) 个数,状态为 \(j\) 的方案数,转移就枚举 \(i\) 要选什么就可以了。

时间复杂度:\(O(2^n\times n^2)\)

代码

#include<bits/stdc++.h>
using namespace std;
int n,f[1<<18][18],ans,mod;
int main(){
	cin>>n>>mod;
	for(int i=0;i<n;i++)f[1<<i][i]=1;
	for(int i=1;i<(1<<n);i++){
		int cnt=0;
		for(int j=0;j<n;j++)cnt+=(i>>j&1);
		if(cnt<=1)continue;
		for(int j=0;j<n;j++){
			if(i>>j&1^1)continue;
			if(cnt&1){
				for(int k=0;k<j;k++){
					if(i>>k&1^1)continue;
					(f[i][j]+=f[i^(1<<j)][k])%=mod;
				}
			}
			else{
				for(int k=j+1;k<n;k++){
					if(i>>k&1^1)continue;
					(f[i][j]+=f[i^(1<<j)][k])%=mod;
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<n;i++)(ans+=f[(1<<n)-1][i])%=mod;
	cout<<(ans<<1)%mod;
	return 0;
}

solution3:\(70\) pts

我没想出来什么 \(O(n^3)\) 的做法,如果你知道,那就来跟我说说把。

代码(来源于叶佳诚)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct IO{
    static const int S=1<<21;
    char buf[S],*p1,*p2;int st[105],Top;
    ~IO(){clear();}
    inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
    inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='r');return *this;}
    template<typename T>inline IO&operator >> (T&x){
        x=0;bool f=0;char ch=gc();
        while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
        f?x=-x:0;return *this;
    }
    inline IO&operator << (const char c){pc(c);return *this;}
    template<typename T>inline IO&operator << (T x){
        if(x<0) pc('-'),x=-x;
        do{st[++st[0]]=x%10,x/=10;}while(x);
        while(st[0]) pc('0'+st[st[0]--]);return *this;
    }
}fin,fout;
const int N=2020;
int f[N][N][2],n,m,T,p;
void init()
{
	f[1][1][1]=f[1][1][0]=1;
	for(register int i=2;i<=n;i++)
	    for(register int j=1;j<=i;j++)
        {
	        for(register int k=1;k<j;k++) f[i][j][1]=(f[i][j][1]+f[i-1][k][0])%p;
	        for(int k=j;k<i;k++) f[i][j][0]=(f[i][j][0]+f[i-1][k][1])%p;
		}
}
int main()
{
//	freopen("ex_B2.in","r",stdin);
//	freopen("nlc.out","w",stdout);
	fin>>n>>p;init();
	int ans=0;
	for(register int i=1;i<=n;i++) ans=(ans+f[n][i][0]+f[n][i][1])%p;
	fout<<ans<<'\n';
	return 0;
}

solution4:\(100\) pts

由于波动序列的限制,考虑消除这个限制。

\(f_{i}\) 为长度为 \(i\) 的先升后降的波动序列个数(即要求 \(a_1<a_2\),这样转移方便)。

初始化:\(f_1=1\)

转移的话,就枚举 \(i\) 这个数放在 \(j\) 的位置,那么方案数就是 \(f_{j-1}\times f_{n-j}\times C_{i-1}^{j-1},(j=2k+1)\)(因为这样固定了前一段和后一段是先升后降还是先降后升,两者方案数相同,\(j=2k+1\) 的时候才能保证前一段为先升后降)。

时间复杂度:\(O(n^2)\)

代码

#include<cstdio>
using namespace std;
const int N=4210;
int n,p,c[N][N],f[N];
int main(){
//	freopen("ex_B1.in","r",stdin);freopen("ex_B1.out","w",stdout);
    scanf("%d%d",&n,&p);
    for(int i=0;i<=n;i++){
        c[i][0]=1;
        for(int j=1;j<=i;j++){
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%p;
        }
    }
    f[0]=f[1]=1;
    for(int i=2;i<=n;i++){
        for(int j=1;j<i;j+=2){
            (f[i]+=1ll*f[j]*f[i-j-1]%p*c[i-1][j]%p)%=p;
        }
    }
    return printf("%d\n",f[n]*2%p),0;
}

C

无原题。

solution1:\(20\) pts

可以发现,如果第 \(i\) 个操作撤销了最近的 \(x\) 次操作,其实就是继承第 \(i-x-1\)次操作后的结果。

如果是加或乘,那就是从上一个操作后的结果继承过来。

那么就可以对于每一次操作,直接继承过来,然后再进行暴力查询或修改即可。

时间复杂度:\(O(nm)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef unsigned int ui;
int n,m;ui a[N];char op[10];
struct ques{int op,l,r;ui x;}q[N];
namespace Solve1{
	ui v[1010][1010];
	void solve(){
		for(int i=1;i<=n;i++)v[0][i]=a[i];
		for(int i=1;i<=m;i++){
			int las=i-1;if(q[i].op==2)las=i-q[i].x-1;
			memcpy(v[i],v[las],sizeof(v[i]));
			if(q[i].op==1){
				for(int j=q[i].l;j<=q[i].r;j++){
					v[i][j]+=q[i].x;
				}
			}
			if(q[i].op==3){
				ui x=0;
				for(int j=q[i].l;j<=q[i].r;j++){
					x+=v[i][j];
				}
				cout<<x<<endl;
			}
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++){
		cin>>op;
		if(op[0]=='A')q[i].op=1,cin>>q[i].l>>q[i].r>>q[i].x;
		else if(op[0]=='C')q[i].op=2,cin>>q[i].x;
		else q[i].op=3,cin>>q[i].l>>q[i].r;
	}
	if(n<=1000)return Solve1::solve(),0;
	return 0;
}

solution2: \(30\) pts

\(n,m\le 2000\) 时,solution1。

对于无撤销操作的数据,直接线段树就好了。

时间复杂度:\(O(m\log n)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct IO{
	#define Tp template<typename T>
	#define D isdigit(c=gc())
	#define precision(x) (fout.bs=x,"")
    static const int S=1<<21;char buf[S],*p1,*p2;int st[105],Top,bs;IO& operator << (const char c){pc(c);return *this;}
    ~IO(){clear();}void clear(){fwrite(buf,1,Top,stdout);Top=0;}void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}IO(){bs=6;}
    IO& operator >> (char&c){while(T(c=gc()));return *this;}int P(char c){return c=='\n'||c=='\r'||c==EOF;}
    IO& operator >> (string&s){s="";char c;while(T(c=gc()));while(s+=c,!T(c=gc()));return *this;}
    IO& operator >> (char*c){while(T(*c=gc()));while(!T(*++c=gc()));return *c=' ',*this;}int T(char c){return c==' '||P(c);}
    IO& operator >> (double&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=x*10+(c^48),D);
		if(c!='.')return *this;c=gc();double k=1;while(x+=(c^48)*(k*=0.1),D);f&&(x=-x);return *this;}
    Tp IO& operator >> (T&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=(x<<3)+(x<<1)+(c^48),D);f&&(x=-x);return *this;}
    IO& operator << (string s){int len=s.length();for(int i=0;i<len;i++)pc(s[i]);return *this;}
    IO& operator << (char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (const char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (double x){x<0&&(pc('-'),x=-x);if(!bs)return *this<<ll(x+0.5);*this<<ll(x)<<'.';x-=ll(x);
		for(int i=1;i<bs;i++)pc(int(x*=10)+'0'),x-=int(x);pc(int(x*10+0.5)+'0');return *this;}
    Tp IO& operator << (T x){x<0&&(pc('-'),x=-x);while(st[++st[0]]=x%10,x/=10);while(st[0])pc(48^st[st[0]--]);return *this;}
    friend IO& getline(IO&io,string&s){s="";char c;while(io.P(c=io.gc()));while(s+=c,!io.P(c=io.gc()));return io;}
    friend IO& getline(IO&io,char*c){while(io.P(*c=io.gc()));while(!io.P(*++c=io.gc()));return *c=' ',io;}
}fin,fout;
const int N=1e6+10;
typedef unsigned int ui;
int n,m;ui a[N];char op[10];
struct ques{int op,l,r;ui x;}q[N];
namespace Solve1{
	ui v[1010][1010];
	void solve(){
		for(int i=1;i<=n;i++)v[0][i]=a[i];
		for(int i=1;i<=m;i++){
			int las=i-1;if(q[i].op==2)las=i-q[i].x-1;
			memcpy(v[i],v[las],sizeof(v[i]));
			if(q[i].op==1){
				for(int j=q[i].l;j<=q[i].r;j++){
					v[i][j]+=q[i].x;
				}
			}
			if(q[i].op==3){
				ui x=0;
				for(int j=q[i].l;j<=q[i].r;j++){
					x+=v[i][j];
				}
				fout<<x<<'\n';
			}
		}
	}
}
namespace SegTree{
	#define RT int l=1,int r=n,int rt=1
	#define mid ((l+r)>>1)
	#define ls rt<<1
	#define rs rt<<1|1
	#define LS l,mid,ls
	#define RS mid+1,r,rs
	#define pd PD(rt,mid-l+1,r-mid)
	ui s[N<<2],f[N<<2];void AD(int rt,ui siz,ui x){s[rt]+=siz*x;f[rt]+=x;}void PU(int rt){s[rt]=s[ls]+s[rs];}
	void PD(int rt,int siz_l,int siz_r){f[rt]&&(AD(ls,siz_l,f[rt]),AD(rs,siz_r,f[rt]),f[rt]=0);}
	void B(RT){l==r?s[rt]=a[l]:(B(LS),B(RS),PU(rt),0);}
	void U(int H,int T,ui x,RT){H<=l&&r<=T?AD(rt,r-l+1,x):(pd,H<=mid&&(U(H,T,x,LS),0),mid<T&&(U(H,T,x,RS),0),PU(rt));}
	ui Q(int H,int T,RT){return H<=l&&r<=T?s[rt]:(pd,(H<=mid?Q(H,T,LS):0)+(mid<T?Q(H,T,RS):0));}
}using namespace SegTree;
namespace Solve2{
	void solve(){
		B();
		for(int i=1;i<=m;i++){
			if(q[i].op==1){
				U(q[i].l,q[i].r,q[i].x);
			}
			else if(q[i].op==3){
				fout<<Q(q[i].l,q[i].r)<<'\n';
			}
		}
	}
}
int main(){
	fin>>n>>m;
	for(int i=1;i<=n;i++)fin>>a[i];
	bool flag=1;int las=0;
	for(int i=1;i<=m;i++){
		fin>>op;
		if(op[0]=='A')q[i].op=1,fin>>q[i].l>>q[i].r>>q[i].x,flag&=(las<=i-q[i].x);
		else if(op[0]=='C')q[i].op=2,fin>>q[i].x,las=i;
		else q[i].op=3,fin>>q[i].l>>q[i].r;
	}
	if(n<=1000)return Solve1::solve(),0;
	if(flag)return Solve2::solve(),0;
	return 0;
}

solution3:\(40\) pts

\(n,m\le 2000\) 时,solution1。

对于撤销操作,直接把要撤销的这些操作减掉就好了。

易得,处理撤销操作的复杂度是 \(O(m\log n)\) 的。

时间复杂度:\(O(m\log n)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct IO{
	#define Tp template<typename T>
	#define D isdigit(c=gc())
	#define precision(x) (fout.bs=x,"")
    static const int S=1<<21;char buf[S],*p1,*p2;int st[105],Top,bs;IO& operator << (const char c){pc(c);return *this;}
    ~IO(){clear();}void clear(){fwrite(buf,1,Top,stdout);Top=0;}void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}IO(){bs=6;}
    IO& operator >> (char&c){while(T(c=gc()));return *this;}int P(char c){return c=='\n'||c=='\r'||c==EOF;}
    IO& operator >> (string&s){s="";char c;while(T(c=gc()));while(s+=c,!T(c=gc()));return *this;}
    IO& operator >> (char*c){while(T(*c=gc()));while(!T(*++c=gc()));return *c=' ',*this;}int T(char c){return c==' '||P(c);}
    IO& operator >> (double&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=x*10+(c^48),D);
		if(c!='.')return *this;c=gc();double k=1;while(x+=(c^48)*(k*=0.1),D);f&&(x=-x);return *this;}
    Tp IO& operator >> (T&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=(x<<3)+(x<<1)+(c^48),D);f&&(x=-x);return *this;}
    IO& operator << (string s){int len=s.length();for(int i=0;i<len;i++)pc(s[i]);return *this;}
    IO& operator << (char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (const char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (double x){x<0&&(pc('-'),x=-x);if(!bs)return *this<<ll(x+0.5);*this<<ll(x)<<'.';x-=ll(x);
		for(int i=1;i<bs;i++)pc(int(x*=10)+'0'),x-=int(x);pc(int(x*10+0.5)+'0');return *this;}
    Tp IO& operator << (T x){x<0&&(pc('-'),x=-x);while(st[++st[0]]=x%10,x/=10);while(st[0])pc(48^st[st[0]--]);return *this;}
    friend IO& getline(IO&io,string&s){s="";char c;while(io.P(c=io.gc()));while(s+=c,!io.P(c=io.gc()));return io;}
    friend IO& getline(IO&io,char*c){while(io.P(*c=io.gc()));while(!io.P(*++c=io.gc()));return *c=' ',io;}
}fin,fout;
const int N=1e6+10;
typedef unsigned int ui;
int n,m;ui a[N];char op[10];
struct ques{int op,l,r;ui x;}q[N];
namespace Solve1{
	ui v[1010][1010];
	void solve(){
		for(int i=1;i<=n;i++)v[0][i]=a[i];
		for(int i=1;i<=m;i++){
			int las=i-1;if(q[i].op==2)las=i-q[i].x-1;
			memcpy(v[i],v[las],sizeof(v[i]));
			if(q[i].op==1){
				for(int j=q[i].l;j<=q[i].r;j++){
					v[i][j]+=q[i].x;
				}
			}
			if(q[i].op==3){
				ui x=0;
				for(int j=q[i].l;j<=q[i].r;j++){
					x+=v[i][j];
				}
				fout<<x<<'\n';
			}
		}
	}
}
namespace SegTree{
	#define RT int l=1,int r=n,int rt=1
	#define mid ((l+r)>>1)
	#define ls rt<<1
	#define rs rt<<1|1
	#define LS l,mid,ls
	#define RS mid+1,r,rs
	#define pd PD(rt,mid-l+1,r-mid)
	ui s[N<<2],f[N<<2];void AD(int rt,ui siz,ui x){s[rt]+=siz*x;f[rt]+=x;}void PU(int rt){s[rt]=s[ls]+s[rs];}
	void PD(int rt,int siz_l,int siz_r){f[rt]&&(AD(ls,siz_l,f[rt]),AD(rs,siz_r,f[rt]),f[rt]=0);}
	void B(RT){l==r?s[rt]=a[l]:(B(LS),B(RS),PU(rt),0);}
	void U(int H,int T,ui x,RT){H<=l&&r<=T?AD(rt,r-l+1,x):(pd,H<=mid&&(U(H,T,x,LS),0),mid<T&&(U(H,T,x,RS),0),PU(rt));}
	ui Q(int H,int T,RT){return H<=l&&r<=T?s[rt]:(pd,(H<=mid?Q(H,T,LS):0)+(mid<T?Q(H,T,RS):0));}
}using namespace SegTree;
namespace Solve2{
	void solve(){
		B();
		for(int i=1;i<=m;i++){
			if(q[i].op==1){
				U(q[i].l,q[i].r,q[i].x);
			}
			else if(q[i].op==3){
				fout<<Q(q[i].l,q[i].r)<<'\n';
			}
		}
	}
}
namespace Solve3{
	void solve(){
		B();
		for(int i=1;i<=m;i++){
			if(q[i].op==1){
				U(q[i].l,q[i].r,q[i].x);
			}
			else if(q[i].op==2){
				for(int j=i-q[i].x;j<i;j++){
					U(q[j].l,q[j].r,-q[j].x);
				}
			}
			else if(q[i].op==3){
				fout<<Q(q[i].l,q[i].r)<<'\n';
			}
		}
	}
}
int main(){
	fin>>n>>m;
	for(int i=1;i<=n;i++)fin>>a[i];
	bool flag=1,flag2=1;int las=0;
	for(int i=1;i<=m;i++){
		fin>>op;
		if(op[0]=='A')q[i].op=1,fin>>q[i].l>>q[i].r>>q[i].x;
		else if(op[0]=='C')q[i].op=2,fin>>q[i].x,flag&=(las<=i-q[i].x),las=i,flag2=0;
		else q[i].op=3,fin>>q[i].l>>q[i].r;
	}
	if(n<=1000)return Solve1::solve(),0;
	if(flag2)return Solve2::solve(),0;
	if(flag)return Solve3::solve(),0;
	return 0;
}

solution4:\(70\) pts

\(n,m\le 10^5\) 时,直接主席树即可。

其他情况:solution3+solution1。

时空复杂度:\(O(m\log n)\)

代码(其他的前 \(40\) 分就不贴了)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct IO{
	#define Tp template<typename T>
	#define D isdigit(c=gc())
	#define precision(x) (fout.bs=x,"")
    static const int S=1<<21;char buf[S],*p1,*p2;int st[105],Top,bs;IO& operator << (const char c){pc(c);return *this;}
    ~IO(){clear();}void clear(){fwrite(buf,1,Top,stdout);Top=0;}void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}IO(){bs=6;}
    IO& operator >> (char&c){while(T(c=gc()));return *this;}int P(char c){return c=='\n'||c=='\r'||c==EOF;}
    IO& operator >> (string&s){s="";char c;while(T(c=gc()));while(s+=c,!T(c=gc()));return *this;}
    IO& operator >> (char*c){while(T(*c=gc()));while(!T(*++c=gc()));return *c=' ',*this;}int T(char c){return c==' '||P(c);}
    IO& operator >> (double&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=x*10+(c^48),D);
		if(c!='.')return *this;c=gc();double k=1;while(x+=(c^48)*(k*=0.1),D);f&&(x=-x);return *this;}
    Tp IO& operator >> (T&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=(x<<3)+(x<<1)+(c^48),D);f&&(x=-x);return *this;}
    IO& operator << (string s){int len=s.length();for(int i=0;i<len;i++)pc(s[i]);return *this;}
    IO& operator << (char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (const char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (double x){x<0&&(pc('-'),x=-x);if(!bs)return *this<<ll(x+0.5);*this<<ll(x)<<'.';x-=ll(x);
		for(int i=1;i<bs;i++)pc(int(x*=10)+'0'),x-=int(x);pc(int(x*10+0.5)+'0');return *this;}
    Tp IO& operator << (T x){x<0&&(pc('-'),x=-x);while(st[++st[0]]=x%10,x/=10);while(st[0])pc(48^st[st[0]--]);return *this;}
    friend IO& getline(IO&io,string&s){s="";char c;while(io.P(c=io.gc()));while(s+=c,!io.P(c=io.gc()));return io;}
    friend IO& getline(IO&io,char*c){while(io.P(*c=io.gc()));while(!io.P(*++c=io.gc()));return *c=' ',io;}
}fin,fout;
const int N=1e6+10;
typedef unsigned int ui;
struct tree{
	int ls,rs;ui s,f;
}a[100000000];
int root[N],cnt,v[N],n,m,l,r,x;char op[10];
void pushup(int rt){
	a[rt].s=a[a[rt].ls].s+a[a[rt].rs].s;
}
void pushdown(int rt,ui siz_l,ui siz_r){
	if(a[rt].f){
		a[++cnt]=a[a[rt].ls];a[rt].ls=cnt;
		a[++cnt]=a[a[rt].rs];a[rt].rs=cnt;
		a[a[rt].ls].s+=siz_l*a[rt].f;
		a[a[rt].rs].s+=siz_r*a[rt].f;
		a[a[rt].ls].f+=a[rt].f;
		a[a[rt].rs].f+=a[rt].f;
		a[rt].f=0;
	}
}
void build(int &rt,int l=1,int r=n){
	rt=++cnt;
	if(l==r)return a[rt].s=v[l],void();
	int mid=(l+r)>>1;
	build(a[rt].ls,l,mid);
	build(a[rt].rs,mid+1,r);
	pushup(rt);
}
void update(int head,int tail,ui x,int &rt,int l=1,int r=n){
	a[++cnt]=a[rt];rt=cnt;
	if(head<=l&&r<=tail)return a[rt].s+=ui(r-l+1)*x,a[rt].f+=x,void();
	int mid=(l+r)>>1;
	pushdown(rt,mid-l+1,r-mid);
	if(head<=mid)update(head,tail,x,a[rt].ls,l,mid);
	if(mid<tail)update(head,tail,x,a[rt].rs,mid+1,r);
	pushup(rt);
}
ui query(int head,int tail,int rt,int l=1,int r=n){
	if(head<=l&&r<=tail)return a[rt].s;
	int mid=(l+r)>>1;
	pushdown(rt,mid-l+1,r-mid);ui s=0;
	if(head<=mid)s+=query(head,tail,a[rt].ls,l,mid);
	if(mid<tail)s+=query(head,tail,a[rt].rs,mid+1,r);
	return s;
}
int main(){
//	freopen("ex_C2.in","r",stdin);freopen("ex_C2.out","w",stdout);
	fin>>n>>m;
	for(int i=1;i<=n;i++)fin>>v[i];
	build(root[0]);
	for(int i=1;i<=m;i++){
		fin>>op;
		if(op[0]=='A'){
			fin>>l>>r>>x;
			update(l,r,x,root[i]=root[i-1]);
		}
		else if(op[0]=='C'){
			fin>>x;
			root[i]=root[i-x-1];
		}
		else{
			fin>>l>>r;
			fout<<query(l,r,root[i]=root[i-1])<<'\n';
		}
	}
	return 0;
}

solution5:\(100\) pts

根据操作之间的继承情况建出一颗树,\(0\) 号节点为初始状态,直接 dfs,到一个操作那就执行,回溯时消除影响就好了。

维护一个线段树即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct IO{
	#define Tp template<typename T>
	#define D isdigit(c=gc())
	#define precision(x) (fout.bs=x,"")
    static const int S=1<<21;char buf[S],*p1,*p2;int st[105],Top,bs;IO& operator << (const char c){pc(c);return *this;}
    ~IO(){clear();}void clear(){fwrite(buf,1,Top,stdout);Top=0;}void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}IO(){bs=6;}
    IO& operator >> (char&c){while(T(c=gc()));return *this;}int T(char c){return c==' '||c=='\n'||c=='\r'||c==EOF;}
    IO& operator >> (string&s){s="";char c;while(T(c=gc()));while(s+=c,!T(c=gc()));return *this;}
    IO& operator >> (char*c){while(T(*c=gc()));while(!T(*++c=gc()));return *c=' ',*this;}
    IO& operator >> (double&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=x*10+(c^48),D);
		if(c!='.')return *this;c=gc();double k=1;while(x+=(c^48)*(k*=0.1),D);f&&(x=-x);return *this;}
    Tp IO& operator >> (T&x){x=0;bool f=0;char c;while(!D)f^=(c=='-');while(x=(x<<3)+(x<<1)+(c^48),D);f&&(x=-x);return *this;}
    IO& operator << (string s){int len=s.length();for(int i=0;i<len;i++)pc(s[i]);return *this;}
    IO& operator << (char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (const char*c){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);return *this;}
    IO& operator << (double x){x<0&&(pc('-'),x=-x);if(!bs)return *this<<ll(x+0.5);*this<<ll(x)<<'.';x-=ll(x);
		for(int i=1;i<bs;i++)pc(int(x*=10)+'0'),x-=int(x);pc(int(x*10+0.5)+'0');return *this;}
    Tp IO& operator << (T x){x<0&&(pc('-'),x=-x);while(st[++st[0]]=x%10,x/=10);while(st[0])pc(48^st[st[0]--]);return *this;}
}fin,fout;
const int N=1e6+10;char s[10];
#define edg(i,u) for(int i=head[u],v;v=edge[i].to,i;i=edge[i].nex)
struct edges{int to,nex;}edge[N];int head[N],kk;void add(int u,int v){edge[++kk]=(edges){v,head[u]};head[u]=kk;}
int n,m,a[N];typedef unsigned int ui;ui c[2][N];
void add(int op,int x,ui y){for(;x<=n;x+=(x&-x))c[op][x]+=y;}
ui get(int op,int x){ui sum=0;for(;x;x-=(x&-x))sum+=c[op][x];return sum;}
ui sum(int x){return x*get(0,x)-get(1,x);}
void update(ui x,ui y,ui z){add(0,x,z),add(0,y+1,-z),add(1,x,(x-1)*z),add(1,y+1,-y*z);}
ui query(ui x,ui y){return sum(y)-sum(x-1);}
struct ques{int op,l,r;ui x;}q[N];
void solve(int u){
	if(q[u].op==1)update(q[u].l,q[u].r,q[u].x);
	else if(q[u].op==3)q[u].x=query(q[u].l,q[u].r);
	edg(i,u)solve(v);
	if(q[u].op==1)update(q[u].l,q[u].r,-q[u].x);
}
int main(){
	fin>>n>>m;for(int i=1,las=0;i<=n;i++)fin>>a[i],add(0,i,a[i]-las),add(1,i,ui(i-1)*ui(a[i]-las)),las=a[i];
	for(int i=1;i<=m;i++){
		fin>>s;
		if(s[0]=='A')q[i].op=1,fin>>q[i].l>>q[i].r>>q[i].x,add(i-1,i);
		else if(s[0]=='C')q[i].op=2,fin>>q[i].x,add(i-q[i].x-1,i);
		else q[i].op=3,fin>>q[i].l>>q[i].r,add(i-1,i);
	}
	solve(0);
	for(int i=1;i<=m;i++)q[i].op==3&&(fout<<q[i].x<<'\n',0);
	return 0;
}

D

原题:link

solution1:\(20\) pts

枚举所有情况验证即可。

时间复杂度:\(O(C_{2n}^n)\)

代码

solution2:\(60\) pts

\(dp_i\)\(2i\) 个点的配对数。

显然,答案是 \(dp_n\)

引理:表示 \(x\) 为与点 \(1\) 匹配的点。注意每个点 \(p(x<p≤2n)\) 属于长度等于 \([1,x]\) 长度的段。

证明:假设某点 \(p(x<p≤2n)\) 与点 \(q(q>p)\) 配对,因为 \([p,q]\)不在\([1,x]\) 之内,所以它们的大小必须相等,配对才是好的。

要计算 \(dp_n\),考虑以下情况:

\(x>n\):类似于上面提到的引理,可以证明每个点 \(p(1≤p≤2n−x+1)\)与点 \(i+x-1\) 配对,剩余的未配对 \(x−n−1\) 点形成一个连续的子阵列,位于每个当前对内,因此它们可以在 \(dp_{x-n-1}\) 中配对种方式。

\(x≤n\):在这种情况下,由于上面提到的引理,所有的线段必须具有相同的长度,因此它们的长度必须是 \(n\) 的约数,在这种情况下,它们可以以 \(D_n\)方式配对;其中 \(D_n\)\(n\) 的约数。

所以 \(dp_n=D_n+∑^{n−1}_{i=0}dp_i\)

请注意,\(dp_0=dp_1=1\)

计算 \(D_i\) 就直接用 \(j\) 枚举 \(j\) 的倍数就好了,因为是调和级数,所以计算 \(D_i\) 的复杂度是 \(O(n\log n)\)

时间复杂度:\(O(n^2)\),瓶颈在于 \(n^2\) 的转移。

代码

#include<cstdio>
using namespace std;
typedef long long ll;
const int mod=998244353,N=1e7+10;
int n,f[N];
int get(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		f[i]++;
		for(int j=(i<<1);j<=n;j+=i){
			f[j]++;
		}
	}
	f[1]=1;
	for(int i=2;i<=n;i++){
		for(int j=1;j<i;j++){
			(f[i]+=f[j])%=mod;
		}
	}
	return printf("%d",f[n]),0;
}
int main(){
    return get();
}

solution3:\(80\) pts

转移直接 \(O(n)\) 记一下前缀和边更新边转移就行了。

时间复杂度:\(O(n\log n)\),瓶颈在于求 \(D_i\)

代码

#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
 
#define X first
#define Y second
#define endl '\n'
 
const int N = 1e7 + 10;
const int MOD = 998244353;
 
int n, dp[N], S;
 
int main() {
    ios_base::sync_with_stdio(false); cin.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = i + i; j <= n; j += i) {
            dp[j]++;
        }
    }
    dp[0] = S = 1;
    for (int i = 1; i <= n; i++) {
        dp[i] = (dp[i] + S) % MOD;
        S = (S + dp[i]) % MOD;
    }
    cout << dp[n] << endl;
    return 0;
}

solution4:\(100\) pts

用线性筛算出所有的 \(D_i\) 就可以了。

维护 \(i\) 最小的约数出现了几次,套用公式:

约数个数定理

时间复杂度:\(O(n)\)

代码

#include<cstdio>
using namespace std;
typedef long long ll;
const int mod=998244353,N=1e7+10;
int n,prime[N],vis[N],cnt[N],f[N];
int get(){
	scanf("%d",&n);
	for(int i=2;i<=n;i++){
		if(!vis[i])prime[++prime[0]]=i,f[i]=2,cnt[i]=1;
		for(int j=1;j<=prime[0]&&1ll*i*prime[j]<=n;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0){
				cnt[i*prime[j]]=cnt[i]+1;
				f[i*prime[j]]=f[i]/(cnt[i]+1)*(cnt[i]+2);
				break;
			}
			cnt[i*prime[j]]=1;
			f[i*prime[j]]=f[i]*2;
		}
	}
	cnt[1]=f[1]=vis[1]=1;
	int sum=0;
	for(int i=1;i<=n;i++)f[i]=(f[i]+sum)%mod,sum=(sum+f[i])%mod;
	return printf("%d",f[n]),0;
}
int main(){
    return get();
}
posted @ 2022-06-11 15:30  A_zjzj  阅读(36)  评论(0编辑  收藏  举报