CCPC2018 湖南全国邀请赛补题(DP/并查集/最长上升子序列)(H待补)

博客内容涉及CCPC2018湖南全国邀请赛的补题解析,主要讨论了D题的DP解决方案和E题的并查集应用。D题通过DP计算环中黑白段的组合;E题比赛中误用树链剖分,赛后发现并查集更合适,利用异或处理边的变更。此外,还提及I题的最长上升子序列问题,最初因线段树导致超时,改用记录方法避免了此问题。

D

比赛时的思路是计数,没有想到DP,现在一想其实DP的挺明显的。

这个思路是网上一个我看的比较明白的思路。

题解:

1. 直接把整个环看成相同数目的黑白段的连接(必定是相同数目)。

2. 用DP计算将i个数分解成j段的所有分发的乘积和,dp[i][j]=\sum_{k=1}^{i-j+1}dp[i-k][j-1]*k,dp的时候用前缀和和滚动数组优化。

3.拼接的时候ans=\sum_{i=1}^{min(n,m)}dp[n][i]*dp[m][i]*(n+m)*inv[i]后取模。

#include<bits/stdc++.h>
#define MOD 1000000007
#define N 5005
#define ll long long
using namespace std;
ll qpow(ll x,ll t){ return t==0?1LL:qpow((x*x)%MOD,t>>1)*(t%2?x:1)%MOD; }
ll inv[N];
ll dp[N][N];
ll s1[2][N];
ll s2[2][N];
void init(){
	int n=5000;
	for (int i=1;i<=n;i++) inv[i]=qpow(i,MOD-2);
	for (int i=1;i<=n;i++){ //geshu
		dp[i][1]=i;
		s1[i%2][1]=(s1[1-i%2][1]+dp[i][1])%MOD;
		s2[i%2][1]=(s2[1-i%2][1]+dp[i][1]*(n-i+1) )%MOD;
		for (int j=2;j<=i;j++){
			dp[i][j]=((s2[1-i%2][j-1]-s1[1-i%2][j-1]*(n-i+1))%MOD+MOD)%MOD;
			s1[i%2][j]=(s1[1-i%2][j]+dp[i][j]);
			s2[i%2][j]=(s2[1-i%2][j]+dp[i][j]*(n-i+1))%MOD;
		}
	}
}
int main(){
	init(); 
	ll n,m;
	while (~scanf("%lld%lld",&n,&m)){
		ll ans=0;
		if (n>m)  swap(n,m);
		for (int i=1;i<=n;i++){
			ans=(ans+dp[n][i]*dp[m][i]%MOD*(n+m)%MOD*inv[i]%MOD)%MOD;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

E

比赛的时候想用树链剖分,写了快两百行代码没debug出来比赛结束,赛后发现直接用并查集维护就可以了。

比赛时已经发现的规律:每个边都只会处理一次,因此直接用异或的性质来处理z的改变。

直接每次沿路径向上即可,沿路合并,修改z值。

debug很久的原因是:加边顺序错了……晕

#include<bits/stdc++.h>
#define N 5005
#define MAXLOGV 21
using namespace std;
struct edge{
	int v,next;
}e[N*2];
int tot=0;
int fa[N][MAXLOGV],h[N],dep[N],s[N],z;
int F[N];
void add(int u, int v){
	e[++tot]=(edge){v,h[u]};
	h[u]=tot;
}
void dfs(int u,int f)
{
    fa[u][0]=f;
	dep[u]=dep[f]+1;
	if (f) s[u]=1;
	else s[u]=0;
    for(int i=h[u];~i;i=e[i].next){
        int v=e[i].v;
        if(v==f) continue;
        dfs(v,u);
        s[u]++;
    }
    z^=s[u];
}
void LCA_init(int n){
    memset(fa,0,sizeof(fa));
    dep[0]=0;
    dfs(1,0);
    for(int k=0;k+1<MAXLOGV;++k){
        for(int v=1;v<=n;++v){
            if(fa[v][k]==0) fa[v][k+1]=0;
            else fa[v][k+1]=fa[fa[v][k]][k];
        }
    }
}
int LCA(int u,int v)
{
    if(dep[u]>dep[v]) swap(u,v);
    for(int k=MAXLOGV-1;k>=0;k--){ //改了顺序 
        if( (dep[v]-dep[u])>>k&1){
//        	cout<<fa[v][k]<<endl;
            v=fa[v][k];
        }
    }
    if(u==v) return u;
    for(int k=MAXLOGV-1;k>=0;--k){
        if(fa[u][k]!=fa[v][k]){
            u=fa[u][k];
            v=fa[v][k];
        }
    }
    return fa[u][0];
}

int get(int x){
	return F[x]==x?x:F[x]=get(F[x]);
}
int unit(int x, int y){
	int fx=get(x);
	int fy=get(y);
	F[fx]=fy;
}
void work(int x, int y){
//	cout<<"merge "<<x<<" "<<y<<endl;
	int fx=get(x);
	int fy=get(y);
	while (fx!=fy){
//		cout<<"fx "<<fx<<" fy "<<fy<<endl;
		if (fa[fx][0]!=y){
//			cout<<"#"<<fa[fx][0]<<" "<<s[fa[fx][0]]<<"->"<<s[fa[fx][0]]-1<<endl;
			if (dep[fx]<=dep[fy]) while (1);
			z^=s[fa[fx][0]];
			s[fa[fx][0]]--;
			z^=s[fa[fx][0]];
			unit(fx,fa[fx][0]);
			fx=get(fx);
		}
		else{
			break;
		} 
	}
}
int main(){
	int n,m,a,b,x,y,u,v;
	while (scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&x,&y)!=EOF){
		memset(h,-1,sizeof(h));
		tot=0;
		z=0;
		for (int i=1;i<=n;i++) F[i]=i;
		for (int i=1;i<=n-1;i++){
			scanf("%d%d",&u,&v);
			add(u+1,v+1);
			add(v+1,u+1);
		}
		LCA_init(n);
		for (int i=1;i<=m;i++){
			int nx=(a*x+b*y+z)%n;
			int ny=(b*x+a*y+z)%n;
//			cout<<"nxny "<<nx<<" "<<ny<<" "<<z<<endl; 
			x=nx;
			y=ny;
			work(x+1,LCA(x+1,y+1));
		}
		printf("%d %d\n",x,y);
	}
	return 0;
}

/*

5 10 5 5 4 0
0 1
1 2
2 3
3 4
2 2 
5 25 1 1 3 0
0 1
0 2
1 3
1 4

*/

I

这是短期内第二次使用线段树超时了,我无话可说。

改用用了\small O(1)的记录方法,记录每个数字的情况,不会超时。

思路:

1. 先处理以每个数字为结尾的最长上升子序列,再处理以每个数字为开头的最长上升子序列。

2. 然后从后往前,用一个数组保存后面的数字的,最长上升子序列为i的序列的开头最大是多少(为了让答案最优),每找到一个0更新这个零到之前找到的最近的一个零之前的所有情况进数组。

3. 设除去0以外的最长上升子序列的长度为LEN,对于非零的第i个数,[ a[i]+1, l[LEN-len1[i]] -1]的数字都可以让这个序列的最长上升子序列加一。在t数组里O(1)标记一下就可以了。

4.注意考虑只有0的情况!!!!!!!!!!!!!!!!

#include<bits/stdc++.h>
using namespace std;
#define N 100005
int a[N],len1[N],len2[N],ans[N],l[N],t[N];
/*
struct tree{
	int t[N*4],tag[N*4];
	void clear(){
		memset(t,0,sizeof(t));
		memset(tag,0,sizeof(tag));
	}
	void push_down(int k, int l, int r, int m){
		tag[k*2]=tag[k*2+1]=1;
		t[k*2]=m-l+1;
		t[k*2+1]=r-m;
		tag[k]=0;
	}
	void update(int k){
		t[k]=t[k*2]+t[k*2+1];
	}
	void insert(int k, int l, int r, int left, int right){
		if (l>right||r<left) return;
		if (left<=l&&r<=right){
			tag[k]=1;
			t[k]=r-l+1;
			return ;
		}
		int m=(l+r)/2;
		if (tag[k]) push_down(k,l,r,m);
		insert(k*2,l,m,left,right);
		insert(k*2+1,m+1,r,left,right);
		update(k);
	} 
	int query(int k, int l, int r, int left, int right){
		if (l>right||r<left) return 0;
		if (left<=l&&r<=right)return t[k];
		int m=(l+r)/2;
		if (tag[k]) push_down(k,l,r,m);
		return query(k*2,l,m,left,right)+query(k*2+1,m+1,r,left,right);
	}
}t;
*/
int main(){
	int n;
	while (scanf("%d",&n)!=EOF){
		
		memset(t,0,sizeof(t));
		memset(l,-1,sizeof(l));
		memset(ans,0,sizeof(0));
		
		for (int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		
		int len=0;
		for (int i=1;i<=n;i++){
			if (a[i]==0){
				continue;
			}
			if (a[i]>ans[len]){
				ans[++len]=a[i];
				len1[i]=len;
			} 
			else{
				int pos=lower_bound(ans,ans+len+1,a[i])-ans;
				len1[i]=pos;
				ans[pos]=a[i];
			}
		}
		
		bool flag=false;
		long long tmp=1e7;
		for (int i=n;i>=1;i--){
			if (a[i]==0) flag=true;
			if (a[i]!=0&&len1[i]==len&&flag){
				tmp=min((long long)a[i],tmp);
			}
		}
		if (tmp<n){
//			cout<<"1+"<<tmp+1<<" "<<n<<endl;
			t[tmp+1]++;
			t[n+1]--;
		}
		
		len=0;
		ans[0]=-1e7;
		for (int i=n;i>=1;i--){
			if (a[i]==0){
				len2[i]=len;
				continue;
			}
			if (-a[i]>ans[len]){
				ans[++len]=-a[i];
				len2[i]=len;
			}
			else{
				int pos=lower_bound(ans,ans+len+1,-a[i])-ans;
				len2[i]=pos;
				ans[pos]=-a[i];
			}
			//cout<<len2[i]<<endl;
		}
		
//		cout<<"len"<<len<<endl;
		int lst=-1;
		for (int i=n;i>=1;i--){
			if (a[i]==0){
				for (int j=i+1;j<=(lst==-1?n:lst-1);j++){
					l[len2[j]]=max(l[len2[j]],a[j]);
//					cout<<len2[j]<<"->"<<l[len2[j]]<<endl;
				}
				lst=i;
				if (l[len]>1){
//					cout<<"2+"<<1<<" "<<l[len]-1<<endl;
					t[1]++;
					t[l[len]]--;
				}
			}
			else{
				if (lst!=-1&&l[len-len1[i]]>a[i]+1){
//					cout<<"3+"<<a[i]+1<<" "<<l[len-len1[i]]-1<<endl;
					t[a[i]+1]+=1;
					t[l[len-len1[i]]]-=1;
				}
			}
		}
	if (len==0){
		cout<<(n+1)*n/2<<endl;
	}
	else{
		long long  cnt=0;
		tmp=0;
		for (long long i=1;i<=n;i++){
			tmp+=t[i];
//			cout<<"tmp"<<tmp<<endl;
			cnt+=i*(len+(tmp>0?1:0));
		}
		printf("%lld\n",cnt);
	}
	}
} 

H没找到题解且不会写,待补

内容概要:本文详细介绍了一种基于Simulink的表贴式永磁同步电机(SPMSM)有限控制集模型预测电流控制(FCS-MPCC)仿真系统。通过构建PMSM数学模型、坐标变换、MPC控制器、SVPWM调制等模块,实现了对电机定子电流的高精度跟踪控制,具备快速动态响应和低稳态误差的特点。文中提供了完整的仿真建模步骤、关键参数设置、核心MATLAB函数代码及仿真结果分析,涵盖转速、电流、转矩和三相电流波形,验证了MPC控制策略在动态性能、稳态精度和抗负载扰动方面的优越性,并提出了参数自整定、加权代价函数、模型预测转矩控制和弱磁扩速等优化方向。; 适合人群:自动化、电气工程及其相关专业本科生、研究生,以及从事电机控制算法研究与仿真的工程技术人员;具备一定的电机原理、自动控制理论和Simulink仿真基础者更佳; 使用场景及目标:①用于永磁同步电机模型预测控制的教学演示、课程设计或毕业设计项目;②作为电机先进控制算法(如MPC、MPTC)的仿真验证平台;③支撑科研中对控制性能优化(如动态响应、抗干扰能力)的研究需求; 阅读建议:建议读者结合Simulink环境动手搭建模型,深入理解各模块间的信号流向与控制逻辑,重点掌握预测模型构建、代价函数设计与开关状态选择机制,并可通过修改电机参数或控制策略进行拓展实验,以增强实践与创新能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值