暑期集训第十一天(7-2)题解及总结
小总结:
今天早上最先做的是先把昨天的那道恶心的树剖题调出来,直到吃完饭回来我才弄完-_- 。
之后我尝试着把ftp里面的对拍程序改成c++格式的(ftp里面的check.sh老师老师的时候不让下了),在考试之前五分钟终于成功了,一开考就兴奋的打了一遍(然鹅一上午并没有用QAQ)
T1:小猫爬山
这道题老师给出的答案竟然是暴搜???n<=18不应该是状压???看到这道题想到上周做过的一道名为"宝藏"的题目,那道题目之中我们用到一个循环来把一个二进制数字进行拆分,来进行预处理,这道题我们也可以用类似的思想来进行处理,先把不同状态的重量算一下,能装下就把这个dp赋值为1,否则为无穷大,之后对不同二进制数字进行拆分,进行拼接,求出最优解即可(之后看时间效率好像刚刚水过???)
#include<bits/stdc++.h> using namespace std; #define int long long const int N=1<<20; int w[50],trans[N],dp[N]; int lowbit(int x){ return x & -x; } int count(int x){ int cnt=1; while(x){ if(x&1) return cnt; x>>=1; cnt++; } return cnt; } int cal(int x){ int ans=0; while(x){ int now=count(x); ans+=w[now]; x-=lowbit(x); } return ans; } signed main(){ int n,m; scanf("%lld%lld",&n,&m); for(int i=1;i<=n;++i) scanf("%lld",&w[i]); int ed=(1<<n)-1; memset(dp,0x3f,sizeof(dp)); for(int i=0;i<=ed;++i){ int now=cal(i); if(now<=m) dp[i]=1; } for(int i=0;i<=ed;++i){ for(int j=i;j;j=(j-1)&i){ int k=i^j; dp[i]=min(dp[i],dp[j]+dp[k]); } } printf("%lld\n",dp[ed]); return 0; }
T2:猴腮雷
看到这个题目还以为这道题猴赛雷(很厉害),但是其实就是一个板子题,我们曾经做过一道题目叫做"没有上司的舞会",和这道题是一摸一样的,就是一个树归的板子,就不解释什么了.
#include<bits/stdc++.h> using namespace std; #define int long long const int N=1e6+10; struct Node{ int next,to; }edge[N]; int Head[N],tot,w[N]; void Add(int x,int y){ edge[++tot].to=y; edge[tot].next=Head[x]; Head[x]=tot; } int dp[N][2]; void dfs(int u,int fa){ dp[u][1]+=w[u]; for(int i=Head[u];i;i=edge[i].next){ int v=edge[i].to; if(v==fa) continue; dfs(v,u); dp[u][1]+=dp[v][0]; dp[u][0]+=max(dp[v][1],dp[v][0]); } } signed main(){ int n; scanf("%lld",&n); for(int i=1;i<=n;++i) scanf("%lld",&w[i]); int x,y; while(scanf("%lld%lld",&x,&y)==2&&x&&y){ Add(x,y);Add(y,x); } dfs(1,1); printf("%lld\n",max(dp[1][0],dp[1][1])); return 0; }
T3:小烈送菜
看到这道题之后先想了半个小时,又想了十五分钟,默默的看了一下表,默默的看了一下数据范围,之后去打了一个30pts的暴力....
这道题的思维还是挺清奇的,我们想到小烈一个来回一定会把所有的人都送到,我们的价值和就是相邻的分数的乘积,这好像和顺序没有任何关系???于是我们可以把小烈的返回的方向反转,换为两个人从起点向终点进行行走,两个人扫过的范围必须满足所有的人都被送到,于是考虑dp[i][j]表示第一个人在i,另一个在j的情况,由于i,j可以互换,于是有f[i][j]==f[j][i],这样我们就可以始终假设i>j,我们的下一状态dp[i+1][j]可以从i和j两个地方进行转移,但是如果从j转移,另一个人此时一定在i点,于是存在转移:f[i+1][j]=max(f[i+1][j],f[i][j]+a[i]∗a[i+1]),f[i+1][i]=max(f[i+1][i],f[i][j]+a[j]∗a[i+1])
之后就解决了.
#include<bits/stdc++.h> using namespace std; const int N=2505; int a[N],dp[N][N],ans=0; int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++i) for(int j=0;j<i;++j){ dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]); dp[i+1][i]=max(dp[i+1][i],dp[i][j]+a[j]*a[i+1]); } for(int i=0;i<n;++i) ans=max(ans,dp[n][i]+a[n]*a[i]); printf("%d\n",ans); return 0; }
推老师博客++:https://www.cnblogs.com/hbhszxyb/p/13223698.html
T4:Siano
(为什么线段树都这么难调呀QAQ)
我们不难发现草的高度无论何时都具有单调性,长得高的永远高,于是我们考虑排序后用线段数来进行维护区间长度,并用二分来进行查询左边界,但是这样的时间效率是n*log^2的,只能拿到六十分,于是考虑如何去掉二分的过程,我们可以维护一个区间的最大值,(其实就是右儿子的大小,右面永远比左边大),之后进行从右边到左的查询,直到遇到一个小于修改值的,这样我们就可以砍掉一个log,成功晋级为nlogn的时间效率,这样就不会超时啦,但是这道题的代码不怎么好调,最初只有100行,让我调完后就成了150行???其实后来发现我的方法繁琐了,其实是不用单独的修改操作的,这里放一个大佬的代码吧.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define ll long long 5 using namespace std; 6 const int N=5e5+10; 7 struct Tree{ 8 int l,r; 9 ll sum,tar,tard,a,last,Mx,Mi; 10 }T[N<<2]; 11 int a[N]; 12 #define ls rt<<1 13 #define rs rt<<1|1 14 void Build(int rt,int l,int r){ 15 T[rt].l=l;T[rt].r=r;T[rt].tar=-1; 16 if(l==r){ 17 T[rt].a=a[l]; 18 return ; 19 } 20 int mid=l+r>>1; 21 Build(ls,l,mid); 22 Build(rs,mid+1,r); 23 T[rt].a=T[ls].a+T[rs].a; 24 } 25 void pushdown(int rt){ 26 if(T[rt].tar==-1)return; 27 T[ls].sum=T[rt].tar*(T[ls].r-T[ls].l+1); 28 T[rs].sum=T[rt].tar*(T[rs].r-T[rs].l+1); 29 T[ls].tar=T[rs].tar=T[ls].Mx=T[ls].Mi=T[rs].Mx=T[rs].Mi=T[rt].tar; 30 T[ls].tard=T[rs].tard=T[ls].last=T[rs].last=T[rt].tard; 31 T[rt].tar=-1; 32 } 33 ll query(int rt,ll d,ll b){ 34 T[rt].sum+=T[rt].a*(d-T[rt].last); 35 T[rt].Mx+=a[T[rt].r]*(d-T[rt].last); 36 T[rt].Mi+=a[T[rt].l]*(d-T[rt].last); 37 T[rt].last=d; 38 ll ans=0; 39 if(T[rt].Mx<=b)return 0; 40 if(T[rt].Mi>b){ 41 ans+=T[rt].sum-b*(T[rt].r-T[rt].l+1); 42 T[rt].tard=T[rt].last=d; 43 T[rt].tar=b; 44 T[rt].sum=b*(T[rt].r-T[rt].l+1); 45 T[rt].Mx=T[rt].Mi=b; 46 return ans; 47 } 48 pushdown(rt); 49 ans=query(ls,d,b)+query(rs,d,b); 50 T[rt].sum=T[ls].sum+T[rs].sum; 51 T[rt].Mi=T[ls].Mi;T[rt].Mx=T[rs].Mx; 52 return ans; 53 } 54 int main(){ 55 int n,m; 56 scanf("%d%d",&n,&m); 57 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 58 sort(a+1,a+n+1); 59 Build(1,1,n); 60 for(int i=1;i<=m;i++){ 61 ll d,b; 62 scanf("%lld%lld",&d,&b); 63 printf("%lld\n",query(1,d,b)); 64 } 65 }
#include<bits/stdc++.h> using namespace std; #define int long long #define lson (t<<1) #define rson (t<<1|1) #define mid ((l+r)>>1) const int N=5e6+10; int w[N]; int n,m,Max=0,Min=0; 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<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } struct Tree{ int cnt,w,siz,lazycnt,lazygai,grow,Max,maxgrow; }tree[N]; void pushup(int t){ tree[t].w=tree[lson].w+tree[rson].w; tree[t].Max=tree[rson].Max; tree[t].maxgrow=tree[rson].maxgrow; } void build(int t,int l,int r){ tree[t].siz=r-l+1; tree[t].lazygai=-1; if(l==r){ tree[t].grow=tree[t].maxgrow=w[l]; return; } build(lson,l,mid);build(rson,mid+1,r); tree[t].grow=tree[lson].grow+tree[rson].grow; pushup(t); } void pushdown1(int t){ if(!tree[t].lazycnt) return; tree[lson].lazycnt+=tree[t].lazycnt; tree[rson].lazycnt+=tree[t].lazycnt; tree[lson].w+=tree[t].lazycnt*tree[lson].grow; tree[rson].w+=tree[t].lazycnt*tree[rson].grow; tree[lson].Max+=tree[lson].maxgrow*tree[t].lazycnt; tree[rson].Max+=tree[rson].maxgrow*tree[t].lazycnt; tree[t].lazycnt=0; } void pushdown2(int t){ if(tree[t].lazygai==-1) return; tree[lson].lazycnt=tree[rson].lazycnt=0; tree[lson].lazygai=tree[rson].lazygai=tree[t].lazygai; tree[lson].Max=tree[t].lazygai; tree[rson].Max=tree[t].lazygai; tree[lson].w=tree[t].lazygai*tree[lson].siz; tree[rson].w=tree[t].lazygai*tree[rson].siz; tree[t].lazygai=-1; } void Add(int t,int l,int r,int al,int ar,int num){ if(al<=l&&r<=ar){ tree[t].lazycnt+=num; tree[t].w+=num*tree[t].grow; tree[t].Max+=tree[t].maxgrow*num; return; } pushdown2(t); pushdown1(t); if(ar<=mid) Add(lson,l,mid,al,ar,num); else if(al>mid) Add(rson,mid+1,r,al,ar,num); else{ Add(lson,l,mid,al,ar,num);Add(rson,mid+1,r,al,ar,num); } pushup(t); } int search_(int t,int l,int r,int val){ if(l==r) return tree[t].Max>=val?l:(n+1); pushdown2(t);pushdown1(t); if(tree[lson].Max<val) return search_(rson,mid+1,r,val); return search_(lson,l,mid,val); } void change(int t,int l,int r,int cl,int cr,int num){ if(cl<=l&&r<=cr){ tree[t].lazygai=num; tree[t].lazycnt=0; tree[t].w=num*tree[t].siz; tree[t].Max=num; return; } pushdown2(t); pushdown1(t); if(tree[t].lazygai) pushdown2(t); if(cr<=mid) change(lson,l,mid,cl,cr,num); else if(cl>mid) change(rson,mid+1,r,cl,cr,num); else{ change(lson,l,mid,cl,cr,num);change(rson,mid+1,r,cl,cr,num); } pushup(t); } int query(int t,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr){ return tree[t].w; } pushdown2(t); pushdown1(t); if(qr<=mid) return query(lson,l,mid,ql,qr); else if(ql>mid) return query(rson,mid+1,r,ql,qr); else{ return query(lson,l,mid,ql,qr)+query(rson,mid+1,r,ql,qr); } } int erfen(int l,int r,int num){ while(l<r){ int now=query(1,1,n,mid,mid); if(now<=num) l=mid+1; else r=mid; } return r; } int pre=0; signed main(){ n=read();m=read(); for(int i=1;i<=n;++i) w[i]=read(); sort(w+1,w+1+n); build(1,1,n); for(int i=1;i<=m;++i){ int x,y; x=read();y=read(); Add(1,1,n,1,n,x-pre); pre=x; int now=search_(1,1,n,y); if(now==n+1){ printf("0\n");continue; } int ans=query(1,1,n,now,n)-(n-now+1)*y; if(ans>0) printf("%lld\n",query(1,1,n,now,n)-(n-now+1)*y); else{ printf("0\n");continue; } change(1,1,n,now,n,y); } return 0; }
总结:
今天我怎么感觉调了一天的代码QAQ上午调昨天的树剖,以及考试时的线段树,下午一下午就把第四道题调过了,晚上就复习了一点数论的内容,看来代码能力还是有待提高,明天我们不考试,自己可以去尝试着盲打几道线段树,单调队列,树剖的板子题,至少以后不能再在这上面花费大量的时间了,最后,希望明天自己能有一个高的效率吧.