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;
}