25-暑期-来追梦noip-卷1 总结
开题顺序:A-B-C(D 没看)
分配时间:A 1h,B 30min,C 10min
A
预估 100,实际 100。
容易设计一个朴素的 dp,枚举 \(dp_{i,j},dp_{i-1,p}\),然后判断 \(j,p\) 是否符合要求然后转移即可。能够获得 80 pts 的好成绩。
注意到,\(p \le j\) 时 能转移的实际上就是 \(\sum^j_{p=1} dp_{i-1,p}\),前缀和可以轻松维护;对于 \(p \bmod j \neq 0\),可以枚举倍数然后前缀和优化,时间复杂度是调和级数的。
实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=15,K=1e5+5;
const int MOD=99824353;
int n,k,ans;
int a[N],dp[N][K],sum[N];
void DP(){
	for(int i=1;i<=k;i++)
		sum[i]=sum[i-1]+1;
	for(int i=2;i<=n;i++){
		for(int j=1;j<=k;j++){
			dp[i][j]=(dp[i][j]+sum[j])%MOD;
			if(j==1)
				continue;
			for(int p=j+1;p<=k;p+=j)
				dp[i][j]=(dp[i][j]+(sum[min(k,p+j-2)]-sum[p-1])%MOD)%MOD;
		}
		for(int j=1;j<=k;j++)
			sum[j]=sum[j-1]+dp[i][j];
	}
	int res=0;
	for(int i=1;i<=k;i++)
		res=(res+dp[n][i])%MOD;
	cout<<res;
}
signed main(){
	//freopen("T1.in","r",stdin);
	//freopen("T1.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>k;
	DP();
	return 0;
}
B
预估 30,实际 30。
令 \(dp_{i,cnt}\) 表示前 \(i\) 个城池选了恰好 \(cnt\) 个且选第 \(i\) 个城池时的最小贡献。
初始 \(dp_{i,1}=\) 所有其他城池到 \(i\) 的贡献,其余正无穷。
答案 \(\min\{dp_{i,k}\}\)。
转移:
其中,\(p_x\) 表示 \(x\) 位置的人数。
后边那个 \(\sum\) 可以拆一下,得到:
然后这两个 \(\sum\) 可以后缀优化。
实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e2+5;
int n,k,tot,ans=1e18;
int x[N],c[N],pos[N];
int h[N],g[N],dp[N][N];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>k;
	for(int i=0;i<256;i++){
		for(int j=0;j<256;j++)
			dp[i][j]=1e18;
		dp[i][1]=0;
	}
	for(int i=1,x,c;i<=n;i++){
		cin>>x>>c,pos[x]=c;
		for(int j=0;j<256;j++)
			dp[j][1]+=(x-j)*(x-j)*c;
	}
	for(int p=255;p>=0;p--)
		h[p]=h[p+1]+pos[p],g[p]=g[p+1]+pos[p]*p;
	for(int i=0;i<256;i++)
		for(int j=0;j<i;j++)
			for(int cnt=2;cnt<=k;cnt++)
				dp[i][cnt]=min(dp[i][cnt],dp[j][cnt-1]+g[(i+j)/2+1]*2*(j-i)+(i*i-j*j)*h[(i+j)/2+1]);
	int ans=1e18;
	for(int i=0;i<256;i++)
		ans=min(ans,dp[i][k]);
	cout<<ans;
	return 0;
}
总结:
- 子序列类型的 dp:\(dp_{i,j}\) 表示钦定 \(i\) 做且前 \(i\) 个做了恰好 \(j\) 个的最值/方案数。
C
预估 0,实际 0。
\(dp_{i,j}\) 表示前 \(i\) 秒干扰 \(j\) 次的最小钱数,分三种情况:没有红包、干扰、不干扰。分别转移即可。考虑到要求 \(w,d\) 最大,所以优先队列维护即可。
实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5,M=2e2+5;
int n,m,k,ans,tot;
int s[N],t[N],d[N],w[N];
int val[N],tme[N],dp[N][M];
struct NODE{
	int s,t,d,w;
	bool operator < (const NODE &b) const {
		if(w==b.w)
			return d<b.d;
		return w<b.w;
	}
}a[N];
priority_queue<NODE> pq;
bool cmp(NODE &x,NODE &y){
	return x.s<y.s;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	//freopen("T3.in","r",stdin);
	//freopen("T3.out","w",stdout);
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++)
		cin>>a[i].s>>a[i].t>>a[i].d>>a[i].w;
	sort(a+1,a+k+1,cmp);
	for(int i=2;i<=n+1;i++)
		for(int j=0;j<=m;j++)
			dp[i][j]=1e18;
	for(int i=1,j=1;i<=n;i++){
		while(j<=k&&a[j].s==i)
			pq.push(a[j]),j++;
		while(!pq.empty()&&pq.top().t<i)
			pq.pop();
		if(pq.empty())
			for(int p=0;p<=m;p++)
				dp[i+1][p]=min(dp[i+1][p],dp[i][p]);
		else{
			for(int p=0;p<=m;p++)
				dp[pq.top().d+1][p]=min(dp[pq.top().d+1][p],dp[i][p]+pq.top().w);
			for(int p=0;p<m;p++)
				dp[i+1][p+1]=min(dp[i+1][p+1],dp[i][p]);
		}
	}
	int ans=1e18;
	for(int i=0;i<=m;i++)
		ans=min(ans,dp[n+1][i]);
	cout<<ans;
	return 0;
}
总结:
- 
也是子序列类型的 dp,只需要堆维护。 
- 
上一个状态不好找时,考虑扩散型转移。 
D
预估 0,实际 0。
一眼困难题。考虑转化条件:
从条件 2,4 很明显可以看出是一个邻接矩阵状物。
条件 1,3 则说明了每个矩阵元素都是度数,且组成了一个环。
考虑对于所有 \(n\),递推出所有结果。
具体而言,\(n-1 \to n\) 有两种情形:
- 
从最基本的考虑:\(n\) 和 \(n-1\) 中任意一点组成二元环,方案数 \(dp_n=dp_{n-1} \times (n-1)\)。 
- 
多元环,则考虑将两个点并作一个,然后和其他点成环,方案数 \(dp_n=dp_{n-1} \times (n-1)\)。 
- 
我们令并作一个的大点中间连的是虚边,其他为实边。注意到,在后者的情况中,实边和虚边的交换会产生同构的环,这样便产生了重复,因此我们必须删掉一种,即 \((n-1) \times (n-2) \times dp_{n-3}\)。 
实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5;
const int MOD=99824353;
int n,dp[N+7];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	dp[1]=0,dp[2]=dp[3]=1;
	for(int i=4;i<=N;i++){
		dp[i]=(i-1)*dp[i-2]%MOD;
		dp[i]=(dp[i]+(i-1)*dp[i-1]%MOD-(i-1)*(i-2)/2*dp[i-3]%MOD)%MOD; 
	}
	int _;
	cin>>_;
	while(_--){
		int n;
		cin>>n;
		cout<<(dp[n]+MOD)%MOD<<'\n';
	} 
	return 0;
}
总结:
- 
条件较多时转化条件。 
- 
转化为图论模型。 
- 
计数题:不重不漏。 
结语
成绩:100+30+0+0=130,并没有挂分。
问题:子序列类型的 dp 不够熟练。
方案:加强条件转化、子序列类型 dp能力。
以上。
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号