R6饮料AK赛(NOIP模拟赛)/省选专练HDU 5713 K个联通块

我好菜啊100+60+30

滚犊子吧,两天加起来才410搞个屁我一年前都可以考400

不说了,题毕竟比较难

T1还是水题但是比昨天难

这是一个开绝对值不等式的题。

根据对奇数和偶数的最优根的归纳一定有一个解是在原址上

于是上界OnLogn

水过

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef int INT;
#define int long long
const int N=2e5+100;
inline void read(int &x){
	x=0;
	char ch=getchar();
	int f=1;
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
int sum[N]={};
int a[N]={};
int n;
int ans=1e17+7;
INT main(){
	freopen("snake.in","r",stdin);
	freopen("snake.out","w",stdout);
	read(n);
	sum[0]=0;
	for(int i=1;i<=n;i++){
		read(a[i]);
		a[i]=a[i]-i;
//		sum[i]=sum[i-1]+a[i];
	}
	sort(a+1,a+1+n);
//	for(int i=1;i<=n;i++){
//		cout<<a[i]<<" ";
//	}
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+a[i];
	}
//	ans=sum[n];
	for(int i=1;i<=n;i++){
		int tmp=(sum[n]-sum[i]-a[i]*(n-i))+(a[i]*i-sum[i]);
//		cout<<i<<" "<<tmp<<'\n';
		ans=min(ans,tmp);
	}
	cout<<ans;
	return 0;
}

T2对我这种弱鸡来说就有点难度了

这是一个结论题

但是虽然我推出了重要结论但是还是没有满分

30分:枚举三个断点

重要结论:

枚举中间部分断点

左右两端寻找尽可能差小的答案值

显然sum的前缀和是单谷函数

你可以三分值或者二分一阶导函数

考试60分代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef int INT;
#define int long long
const int N=2e5+10;
inline void read(int &x){
	x=0;
	char ch=getchar();
	int f=1;
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
int a[N]={};
int sum[N]={};
int n;
int ans=1e17+9;
struct Node{
	int minsum,maxsum;
};
Node getsum(int L,int R){
	int l=L;
	int r=R;
	int ans=1e16+7;
	Node ret;
	ret.maxsum=-1;
	ret.minsum=1e17;
	for(int i=l;i<r;i++){
		if(ans>(abs((sum[i]-sum[L-1])-(sum[R]-sum[i])))){
			ans=abs((sum[i]-sum[L-1])-(sum[R]-sum[i]));
//			cout<<i<<" "<<(sum[i]-sum[L-1])<<" "<<(sum[R]-sum[i])<<'\n';
			ret.maxsum=max((sum[i]-sum[L-1]),(sum[R]-sum[i]));
			ret.minsum=min((sum[i]-sum[L-1]),(sum[R]-sum[i]));
		}
	}
	return ret;
}
INT main(){
	freopen("scream.in","r",stdin);
	freopen("scream.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	if(n<=300){
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				for(int k=j+1;k<n;k++){
					int maxsum=max(sum[i],max(sum[j]-sum[i],max(sum[k]-sum[j],sum[n]-sum[k])));
					int minsum=min(sum[i],min(sum[j]-sum[i],min(sum[k]-sum[j],sum[n]-sum[k])));
					ans=min(ans,maxsum-minsum);
				}
			}
		}
		cout<<ans;
		return 0;	
	}
	else{
		if(n<=5000){
			for(int i=3;i<n;i++){
				Node tmpleft;
				tmpleft=getsum(1,i-1);
				Node tmpright;
				tmpright=getsum(i,n);
				int maxsum=max(tmpleft.maxsum,tmpright.maxsum);
				int minsum=min(tmpleft.minsum,tmpright.minsum);
//				cout<<i<<" "<<maxsum<<" "<<minsum<<'\n';
				ans=min(ans,maxsum-minsum);
			}
			cout<<ans;
			return 0;
		}
	}
//	cout<<a[n]
	return 0;
}

AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef int INT;
#define int long long
const int N=2e5+10;
inline void read(int &x){
	x=0;
	char ch=getchar();
	int f=1;
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
int a[N]={};
int sum[N]={};
int n;
int ans=1e17+9;
struct Node{
	int minsum,maxsum;
};
Node getsuml(int L,int R){
	int l=L;
	int r=R-1;
	int ans=1e16+7;
	int s=sum[R]-sum[L-1];
	Node ret;
	while(l^r){
		int mid=(l+r)/2;
		if(abs(sum[mid]*2-s)<abs(ans*2-s))ans=sum[mid];
		if(sum[mid]*2<=s)l=mid+1;
		else r=mid;
	}
	if(abs(sum[l]*2-s)<abs(ans*2-s))ans=sum[l];
	ret.maxsum=max(s-ans,ans);
	ret.minsum=min(s-ans,ans);
	return ret;
}
Node getsumr(int L,int R){
	int l=L;
	int r=R-1;
	int ans=1e16+7;
	int s=sum[R]-sum[L-1];
	Node ret;
	while(l^r){
		int mid=(l+r)/2;
		if(abs((sum[n]-sum[mid])*2-s)<abs(ans*2-s))ans=sum[n]-sum[mid];
		if((sum[n]-sum[mid])*2<=s)r=mid;
		else l=mid+1;
	}
	if(abs((sum[n]-sum[l])*2-s)<abs(ans*2-s))ans=(sum[n]-sum[l]);
	ret.maxsum=max(s-ans,ans);
	ret.minsum=min(s-ans,ans);
	return ret;
}
INT main(){
	freopen("scream.in","r",stdin);
	freopen("scream.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(int i=3;i<n;i++){
		Node tmpleft;
		tmpleft=getsuml(1,i-1);
		Node tmpright;
		tmpright=getsumr(i,n);
		int maxsum=max(tmpleft.maxsum,tmpright.maxsum);
		int minsum=min(tmpleft.minsum,tmpright.minsum);
//		cout<<i<<" "<<maxsum<<" "<<minsum<<'\n';
		ans=min(ans,maxsum-minsum);
	}
	cout<<ans;
	return 0;
}

注意此处的二分写法新颖

实际乘二是指的右边除二

原理是我判断其离平均值有多近。

这等价于俩边之差更接近

T3

HDU5713 K个联通块

省选难度状压DP

你不能枚举边

但是枚举点的复杂度是可行的

于是思考一:状压DP

定义状态:

dp(i,j)i:当前有i个联通块 j:点集状态为j即一个状压状态0100101010之类的

f(i)i:一个状压状态表示该点集时可以断掉的边的种类使这个点集依旧连通

G(i)i:一个状压状态表示一个点集,目的是选择后断开(容斥原理)

用全集减补集求出F

考试30分代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int mod=1e9+9;
inline void read(int &x){
	x=0;
	char ch=getchar();
	int f=1;
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
const int N=20;
struct Front_star{
	int u,v,nxt,del;
}e[N*N];
int cnt=0;
int first[N]={};
void add(int u,int v){
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].nxt=first[u];
	e[cnt].del=0;
	first[u]=cnt;
}
int n,m,k;
int ans=0;
int fa[15]={};
inline int getfa(int x){
	if(fa[x]==x)return x;
	else return fa[x]=getfa(fa[x]);
}
inline void Union(int x,int y){
	int dx=getfa(x);
	int dy=getfa(y);
	fa[dy]=dx;
}
int check(){		
	
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=1;i<=cnt;i++){
		if(!e[i].del)
			Union(e[i].u,e[i].v);
	}
	for(int i=1;i<=n;i++){
		getfa(i);
	}
	sort(fa+1,fa+1+n);
	int len=unique(fa+1,fa+1+n)-fa-1;
	if(len==k){
//		for(int i=1;i<=cnt;i++){
//			cout<<e[i].u<<" "<<e[i].v<<" "<<e[i].del<<'\n';
//		}
//		cout<<"---------"<<'\n';
		return 1;
	}
//		return 1;
	return 0;
}
void dfs(int tim){
	if(tim==cnt+1){
		ans+=check();
		ans=ans%mod;
		return;	
	}
//	if(tim>cnt)return;
	e[tim].del=1;
	dfs(tim+1);
	e[tim].del=0;
	dfs(tim+1);
}
int main(){
	freopen("sarsae.in","r",stdin);
	freopen("sarsae.out","w",stdout);
	read(n);
	read(m);
	read(k);
	for(int i=1;i<=m;i++){
		int u,v;
		read(u);
		read(v);
		add(u,v);
	}
	dfs(1);
	cout<<ans;
	return 0;
}

修改后代码

dp更新是一个背包原理

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef int INT;
#define int long long 
inline void read(int &x){
	x=0;
	char ch=getchar();
	int f=1;
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
int f2[1000+20]={};
const int mod=1e9+9;
void Pre(){
	f2[0]=1;
	for(int i=1;i<=1000;i++){
		f2[i]=f2[i-1]*2%mod;
	}
}
const int N=15;
int n,m,k;
int maxn;
int dp[N][1<<N]={};
int f[1<<N]={};
int g[1<<N]={};
int vis[1<<N]={};
int fa[N]={};
inline int lowbit(int x){
	return x&(-x);
}
int getfa(int x){
	if(fa[x]==x)return fa[x];
	else return fa[x]=getfa(fa[x]);
}
void Union(int x,int y){
	int dx=getfa(x);
	int dy=getfa(y);
	fa[dx]=dy;	
}
INT main(){
//	freopen("sarsae.in","r",stdin);
	Pre();
	read(n);
	read(m);
	read(k);
	maxn=(1<<n)-1;
	for(int i=1;i<=m;i++){
		int u,v;
		read(u);
		read(v);
		u--;
		v--;
		for(int j=0;j<=maxn;j++){
			if((j&(1<<u))&&(j&(1<<v))){
//				cout<<"working";
				f[j]++;
			}
		}
		Union(u,v);
	}	
	for(int st=1;st<=maxn;st++){
		int minP=lowbit(st);
		int To=-1;
		int Fail=0;
		for(int j=0;j<n;j++){
			if((1<<j)&st){
				if(To==-1){
					To=getfa(j);		
				}
				else{
					if(To!=getfa(j)){
						Fail=1;
						break;
					}
				}
			}
		}
		if(Fail)continue;
		else vis[st]=1;
		f[st]=f2[f[st]];
		g[st]=f[st];
		for(int j=st;j;j=(j-1)&st){
			if(j==st)continue;
			if(!vis[j])continue;
			if(j&minP){
				f[st]-=f[j]*g[j^st]%mod;
				f[st]%=mod;
			}
		}
	}
//	for(int i=1;i<=maxn;i++){
//		cout<<f[i]<<" ";
//	}
	dp[0][0]=1;
	for(int i=1;i<=k;i++){
		for(int st=1;st<=maxn;st++){
			int p=lowbit(st);
			for(int sub=st;sub;sub=(sub-1)&st){
				if(!vis[sub])continue;
				if(p&sub){
					dp[i][st]+=dp[i-1][st^sub]*f[sub]%mod;
					dp[i][st]%=mod;	
				}	
			}
		}
	} 
	cout<<(dp[k][maxn]+mod)%mod;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值