12.2

12.2

自闭double

又是困得要命的一天,10点前的我是躯体在打代码(乱搞了t1,看了t2没脑子想),10点后才清醒,t3一眼不可做,t4一看树形背包,劈啪乱敲,调过样例,悟到复杂度是\(O(nm^2)\) 本着骗分第一的思想,又去敲了链和菊花图,想着总该能骗70吧,事实只有5分前面wa后面re 回过头看t2 ,一开始存点位置+状态显然爆空间,一想中间点没用,直接记礼物编号就可,然后预处理礼物之间的距离,一顿状压dp ,可惜没调出来自闭啊

t1 最小得分和

挂成了50 呜呜呜

std的正经暴力做法

O(KlogN)堆,首先将每相邻两个元素差值入堆,每次选取当前堆中最小的数,找到它在原序列中的位置i,然后向左找到第一个没有取的数对(i,p[i]),将堆顶元素变为s[i]-s[p[i]-1],s[i]为前缀和,然后dec(p[i]),做K次得到答案。

我是类似的思想 不相似的得分

#include <queue>
#include <cstdio>
#include <ctime>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=1e6+10;
inline ll read() {
	ll x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x;
} 

ll n,k,a[N],ans;
priority_queue<int>q;
int main() {
	n=read();k=read();
	for(int i=1;i<=n;i++) a[i]=read();
	sort(a+1,a+1+n);
	for(int i=2;i<=n;i++) 
		q.push(a[i]-a[i-1]);
	while(q.size()>k) q.pop();
	for(int i=2;i<=n;i++)
		for(int j=i-2;j>=1;j--) {
			if(q.size()<k) q.push(a[i]-a[j]);
			else {
				int x=q.top();
				if(x<=a[i]-a[j]) break;
				else q.pop(),q.push(a[i]-a[j]);
			}
		}
	while(q.size()) 
		ans+=q.top(),q.pop();
	printf("%lld\n",ans);
	return 0;
} 

正解

贪心。
将序列从小到大排序

二分这K对数里差值最大的数相差多少,判断是否有大于等于K对满足条件(用个指针扫一遍——单调)

#include <queue>
#include <cstdio>
#include <ctime>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=1e6+10;
inline ll read() {
	ll x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x;
} 

ll n,k,a[N],ans;
bool check(ll x) {
	ll cnt=0,j=1;
	for(ll i=2;i<=n;i++) {
		while(a[i]-a[j]>x) j++;
		cnt+=i-j;
		if(cnt>=k) return 1;
	}
	return 0;
}
ll sum[N];
int main() {
	// freopen("mark9.in","r",stdin);
	n=read();k=read();
	for(int i=1;i<=n;i++) a[i]=read();
	sort(a+1,a+1+n);
	//二分差值
	ll l=0,r=a[n]-a[1];
	while(l<=r) {
		ll mid=(l+r)>>1;
		if(check(mid)) r=mid-1;
		else l=mid+1;
	}
	//l 是满足k对的最小差值
	ll j=1;ll cnt=0;
	sum[1]=a[1];
	for(ll i=2;i<=n;i++) {
		while(a[i]-a[j]>l) ++j;
		cnt+=i-j;
		ans+=a[i]*(i-j)-sum[i-1]+sum[j-1];
		sum[i]=sum[i-1]+a[i];
	}
	if(cnt>k) ans-=(cnt-k)*l;
	printf("%lld\n",ans);
	return 0;
} 

t2

我想的和正解几乎一毛一样

然而初始化出锅了 ,我只初始化了\(f[1][0]=0\) ,正解的初始化是对每个礼物起点到它的时间都处理

然后我一开始写的 dij ,后来嫌麻烦改成bfs ,后来一想是不是队列慢来着(加上队列版的写挂)然后换成递归bfs,一个显然错的最短路,令我自闭改了1小时,回归dij怀抱 ————没事就写dij ,总不会错

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
// typedef long long int;
#define int long long
const int N=55;
const int M=(1<<17)+5;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
int n,m,T,si,sj,ei,ej;
char mp[N][N];
#define MP make_pair

int f[20][M];
pair<int,int> pos[20];
int mx,num[M];
inline int get(int s) {
	int res=0;
	for(int j=0;j<mx;j++)
		if(s&(1<<j))
			res++;
	return res;
}

const int inf=1e15;
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
int dis[20][20],id[N][N];
bool vis[N][N];
struct node{
	int r,c,d;
	node(){}
	node(int xx,int yy,int dd):r(xx),c(yy),d(dd){}
	bool operator < (const node &w) const {
		return d>w.d;
	}
};
priority_queue<node>q;

void get_dis(int u) {
	vis[pos[u].first][pos[u].second]=1;
	q.push(node(pos[u].first,pos[u].second,0));
	while(q.size()) {
		int x=q.top().r,y=q.top().c,dd=q.top().d;
		q.pop();
		for(int i=0;i<4;i++) {
			int x_=x+dx[i],y_=y+dy[i];
			if(mp[x_][y_]=='#'||x_<1||y_<1||x_>n||y_>m||vis[x_][y_]) continue;
			vis[x_][y_]=1;
			q.push(node(x_,y_,dd+1));
			if(mp[x_][y_]=='G'||mp[x_][y_]=='Q'||mp[x_][y_]=='K') {
				Min(dis[u][id[x_][y_]],dd+1);
				Min(dis[id[x_][y_]][u],dd+1);
			}
		}
	}
}
int ans;
signed main() {
	n=read();m=read();T=read();
	for(int i=1;i<=19;i++)
		for(int j=1;j<=19;j++)
			if(i!=j)
				dis[i][j]=inf;
	for(int i=1;i<=n;i++) {
		scanf("%s",mp[i]+1);
		for(int j=1;j<=m;j++) {
			if(mp[i][j]=='K') 
				si=i,sj=j;
			if(mp[i][j]=='G') {
				id[i][j]=++mx;
				pos[mx]=MP(i,j);
			}
			if(mp[i][j]=='Q') 
				ei=i,ej=j;
		}
	}
	id[si][sj]=mx+1;pos[mx+1]=MP(si,sj);
	id[ei][ej]=mx+2;pos[mx+2]=MP(ei,ej);
	for(int i=1;i<=mx+2;i++) {
		memset(vis,0,sizeof(vis));
		get_dis(i);
	}
	memset(f,0x3f,sizeof(f));
	for(int s=0;s<(1<<mx);s++)
		num[s]=get(s);
	//初始化,就这写挂了爆零了
	for(int i=1;i<=mx;i++) {
		f[i][1<<(i-1)]=dis[mx+1][i];
		if(f[i][(1<<(i-1))]+2*dis[mx+2][i]<=T)
			Max(ans,1);
	}

	for(int s=0;s<(1<<mx);s++) 
		for(int i=1;i<=mx;i++) {
			if(s&(1<<(i-1))) continue;
			for(int j=1;j<=mx;j++) {
				if(f[j][s]>T||i==j) continue;
				if(s&(1<<(j-1))) {
					int now=s|(1<<(i-1));
					Min(f[i][now],f[j][s]+dis[i][j]*(num[s]+1));
					int dd=f[i][now]+(num[s]+2)*dis[mx+2][i];
					if(dd<=T) Max(ans,num[now]);
				}
			}
			
		}
	// for(int i=1;i<=mx+2;i++)
	// 	for(int j=1;j<=mx+2;j++)
	// 		printf("%lld\n",dis[i][j]);
	printf("%lld\n",ans);
	return 0;
}

t3

咕咕

t4 树形背包进化

暴力做法TLE

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=5005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,m;
int f[N][N<<1];
int to[N<<1],nxt[N<<1],hd[N],tot,fa[N];
inline void add(int x,int y) {
	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int v[N],w[N],ans;

void dfs(int x) {
	for(int i=hd[x];i;i=nxt[i]) {
		int y=to[i];
		dfs(y);
		for(int j=m;j>=0;j--) 
			for(int k=0;k<=j;k++) 
				Max(f[x][j],f[y][k]+f[x][j-k]);
	}
    //这里是强制选x为根否则都是0
	for(int j=m;j>=0;j--)
		if(j-v[x]>=0) f[x][j]=f[x][j-v[x]]+w[x];
		else f[x][j]=0;
}
int main() {
	n=read();m=read();
	for(int i=1;i<=n;i++) {
		v[i]=read();
		fa[i]=read();
		if(i!=1) add(fa[i],i);
		w[i]=read();
	}
	dfs(1);
	printf("%d\n",f[1][m]);
	return 0;
}

正解

选课题解里也提到了这种做法

先求出树的后\(dfs\)序,也就是先儿子后父亲 ,\(rev[i]\)\(dfn\)序为\(i\)的点编号

然后显然的转移——见注释

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
#define int long long
const int N=5005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,m;
int f[N][N<<1];
int to[N<<1],nxt[N<<1],hd[N],tot,fa[N];
inline void add(int x,int y) {
	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int v[N],w[N],ans;
int siz[N],rev[N],tim;
void dfs(int x) {
	siz[x]=1;
	for(int i=hd[x];i;i=nxt[i]) {
		dfs(to[i]);
		siz[x]+=siz[to[i]];
	}
	rev[++tim]=x;
}
signed main() {
	n=read();m=read();
	v[1]=read();read();w[1]=read();
	for(int i=2;i<=n;i++) {
		v[i]=read();fa[i]=read();add(fa[i],i);w[i]=read();
	}
	dfs(1);
	for(int i=1;i<=n;i++)
		for(int j=m;j>=0;j--) {
			if(j>=v[rev[i]]) Max(f[i][j],f[i-1][j-v[rev[i]]]+w[rev[i]]);
            //能选它
			if(i>=siz[rev[i]]) Max(f[i][j],f[i-siz[rev[i]]][j]);//不选它就不选它的子树
			//显然它的子树编号都比它小
		}
	for(int i=1;i<=m;i++)	
		Max(ans,f[n][i]);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2020-12-02 19:33  ke_xin  阅读(76)  评论(0编辑  收藏  举报