20160819训练记录
T1
给定一个序列 ,在第i时刻位置右边插入一个数i 求最后的序列
【题解】倒着做。。插入的位置是左边有空格的位置 用线段树维护一下单点修改
线段树写的优雅一点
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int p,f[4444444],g[4444444],ans[4444444]; int find(int i,int l,int r,int x){ f[i]--; if(l==r)return i-p+1; if(f[i<<1]>=x)return find((i<<1),l,(l+r)/2,x); else return find((i<<1|1),(l+r)/2+1,r,x-f[i<<1]); } #define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout); int main(){ FO(sequence); int n;scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&g[i]); for(p=1;p<n;p<<=1)cerr<<p; for(int i=p;i<p+p;i++)f[i]=1; for(int i=p-1;i>=1;i--)f[i]=f[i*2]+f[i*2+1]; cerr<<f[1]<<endl; for(int i=n;i>=1;i--)ans[find(1,1,p,g[i]+1)]=i; for(int i=1;i<=n;i++)printf("%d ",ans[i]); }
T2
0/1背包
额 对于这个问题 ,我们可以先找到前20个物品对于每个重量的最大价值
然后求出后20个的 然后二分一下两个体积合并的最优值。。记得long long
#include<map> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; int n,_p; typedef long long ll; ll m,ans,w[44],v[44],max_val[1200666]; struct data{ ll w,v; }pii[1233333]; inline bool cmp(const data&a,const data&b){ return a.w<b.w||(a.w==b.w&&a.v<b.v); } inline ll max(ll a,ll b){ return a>b?a:b; } int binary_search(ll x){ int l=1,r=_p,mid=1,ans=1; while(l<=r){ mid=(l+r)/2; if(pii[mid].w<=x){ ans=mid; l=mid+1; } else r=mid-1; } return ans; } int main(){ freopen("pack.in","r",stdin); freopen("pack.out","w",stdout); scanf("%d %I64d",&n,&m); for(int i=1;i<=n;i++)scanf("%I64d %I64d",&w[i],&v[i]); int hf=(1<<(n/2)),_hf=0; for(int i=0;i<hf;i++){ ll ww=0,vv=0; for(int k=1;k<=n/2;k++)if(i&(1<<(k-1)))ww+=w[k],vv+=v[k]; if(ww<=m){ data t=(data){ww,vv};pii[++_hf]=t; } } sort(pii+1,pii+_hf+1,cmp); for(int i=1;i<=_hf;i++){ if(pii[i].w!=pii[i+1].w)pii[++_p]=(data)pii[i]; } for(int i=1;i<=_p;i++)max_val[i]=max(max_val[i-1],pii[i].v); //for(int i=1;i<=_p;i++)cout<<pii[i].w<<' '<<pii[i].v<<endl; int fff=(1<<(n/2+(n&1))); for(int i=0;i<fff;i++){ ll ww=0,vv=0; for(int k=1;k<=n/2+(n&1);k++)if(i&(1<<(k-1)))ww+=w[k+(n>>1)],vv+=v[k+(n>>1)]; ll t=m-ww; //cout<<ww<<' '<<vv<<endl; if(t<0)continue; //printf("found(%d) for %d\n",binary_search(t),t); ans=max(ans,max_val[binary_search(t)]+vv); } cout<<ans; return 0; } /* dialog 折半搜索 T(n)=O(2^(n/2)*n) *极限数据还挺快的。。就不优化了 */
T3
n,m<=5000
区间dp+四边形不等式
对于每个节点代表的区间[l,r],求出这个子树中最少访问次数,一个节点被访问一次当且仅当查询区间和他相交
然后这个东西是一个四边形不等式的形式
#include<stdio.h> #include<iostream> #include<stdlib.h> #include<math.h> #include<algorithm> #include<string> #include<string.h> #define il inline #define re register #define lowbit(x) (x&(-x)) using namespace std; typedef long long ll; int n,m,a[100010],b[100010],s[5010],g[5010][5010],end[5010],begin[5010],c[5010][5010]; ll f[5010][5010]; il void init(){ for(int l=2;l<=n;l++){ for(int i=1,j;i<=n-l+1;i++){ j=i+l-1;f[i][j]=(1ll<<50); for(int k=i;k<j;k++){ if(f[i][j]>f[i][k]+f[k+1][j]+g[i][j]){ c[i][j]=k;f[i][j]=f[i][k]+f[k+1][j]+g[i][j]; } } } } cout<<f[1][n]; } int main(){ freopen("segment.in","r",stdin); freopen("segment.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&a[i],&b[i]); end[b[i]]++;begin[a[i]]++; } for(int i=1;i<=n;i++) end[i]+=end[i-1]; for(int i=n;i>=1;i--) begin[i]+=begin[i+1]; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) g[i][j]=m-end[i-1]-begin[j+1]; for(int i=1;i<=n;i++) f[i][i]=g[i][i]; if(n<=200){ init();return 0; } for(int i=1;i<=n;i++) c[i][i]=i; for(int l=2;l<=n;l++){ for(int i=1,j;i<=n-l+1;i++){ j=i+l-1;f[i][j]=(1ll<<50); for(int k=max(i,c[i][j-1]);k<j&&k<=c[i+1][j];k++){ if(f[i][j]>f[i][k]+f[k+1][j]+g[i][j]){ c[i][j]=k;f[i][j]=f[i][k]+f[k+1][j]+g[i][j]; } } } } cout<<f[1][n]; return 0; }