Solution of NOIP2021模拟赛 10.21 By ZJ -- zhengjun
@
A
solution
显然枚举碗的叠放顺序,然后算一下高度就可以了。
高度的话,就处理出 \(i\) 放在 \(j\) 上面的增加的高度。
时间复杂度:\(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
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();
}