NOIP模拟赛20190905

T1

大水题,Skip。

T2 将军令

洛谷P3942 将军令

树,一个点驻队可以控制距离小于等于k的点,求控制所有点的最小驻队数。n<=100000,k<=20

贪心解法:

  • 按深度从大到小排序,拿出最深的一个,如果没有被控制就找k级祖先然后暴力遍历,复杂度O(nk)
  • 维护两个值:下面的驻队点到当前点还能延伸的距离,下面未被控制的点的最深深度,贪心放点。复杂度O(n)

DP解法:

  • f [ u ] [ j ] f[u][j] f[u][j] j &gt; 0 j&gt;0 j>0表示向上至少还能控制的距离, j &lt; 0 j&lt;0 j<0表示向下至多还有深度之差为多少的点需要控制,转移比较麻烦。

我写的O(n)贪心。
Code:

#include<bits/stdc++.h>
#define LL long long
#define maxn 100005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');	
}
template<class T>inline void write(T x){
	if(x>=10) write(x/10);
	putchar(x%10+48);
}
int n,K,type,ans,mxd[maxn],up[maxn];
vector<int>G[maxn];
void dfs(int u,int ff){
	up[u]=-1;
	for(int i=G[u].size()-1,v;i>=0;i--) if((v=G[u][i])!=ff){
		dfs(v,u);
		up[u]=max(up[u],up[v]-1);
		mxd[u]=max(mxd[u],mxd[v]+1);
	}
	if(up[u]>=mxd[u]) mxd[u]=-1;
	else if(mxd[u]==K||u==1) ans++,mxd[u]=-1,up[u]=K;
}
int main()
{
	read(n),read(K),read(type);
	for(int i=1,x,y;i<n;i++) read(x),read(y),G[x].push_back(y),G[y].push_back(x);
	dfs(1,0);
	write(ans),putchar('\n');
}

T3

洛谷P3943 星空

长度小于等于40000的01序列,小于等于8个为1,可以翻转给定长度的区间(种数小于等于64),问最少翻转次数。

区间转化为差分,翻转相当于改两个点,小于等于16个1,bfs预处理翻转每一对1需要的最少次数,状压转移即可。

这篇题解值得一看。

Code:

#include<bits/stdc++.h>
#define maxn 40005
#define maxm 65
#define maxk 18
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,k,a[maxk],b[maxm],dis[maxn],d[maxk][maxk],f[1<<maxk];
bool arr[maxn];
queue<int>q;
void bfs(int S){
	memset(dis,0x3f,sizeof dis);
	dis[S]=0,q.push(S);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=1;i<=m;i++){
			if(u+b[i]<=n+1&&dis[u+b[i]]==inf) dis[u+b[i]]=dis[u]+1,q.push(u+b[i]);
			if(u-b[i]>=1&&dis[u-b[i]]==inf) dis[u-b[i]]=dis[u]+1,q.push(u-b[i]);
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1,x;i<=k;i++) scanf("%d",&x),arr[x]^=1,arr[x+1]^=1;
	for(int i=1;i<=m;i++) scanf("%d",&b[i]);
	k=0;
	for(int i=1;i<=n+1;i++) if(arr[i]) a[k++]=i;
	for(int i=0;i<k-1;i++){
		bfs(a[i]);
		for(int j=i+1;j<k;j++) d[i][j]=dis[a[j]];
	}
	memset(f,0x3f,sizeof f);
	f[0]=0;
	for(int s=1,low;s<1<<k;s++) {
		for(int i=0;;i++) if(s>>i&1) {low=i;break;}
		for(int i=low+1;i<k;i++) if(s>>i&1) f[s]=min(f[s],f[s^(1<<i)^(1<<low)]+d[low][i]);
	}
	printf("%d\n",f[(1<<k)-1]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值