今天又倒数,第二题复制了以后忘改变量名;
第三题文件错误,我竟然也犯了这种错,可能考试将近,心态没调整好;
题解:
思路很棒的DP题。
可以发现每一段中不同的数的个数不超过sqrt(n)个。
所以我们只需要记录每个点为结尾,一段序列中不同的数的个数不超过j的左端点的位置-1,pos[j]。通过pos数组进行DP转移,可以将复杂度从O(n^2)将为O(sqrt(n)n)。f[i]=min{f[pos[j]]+j*j}。
下面我们考虑右端点右移,也就是i++时,如何更新pos数组。
为了快速更新,我们还需要记录每个数值i的最后一个位置pre[i],和以pos[j]为左端点的序列中不同的数的个数cnt[j]。
当i++后,如果pre[a[i]]≤pos[j],那么cnt[j]++,说明序列中加入一个新的元素。
然后我们找出所有cnt[j]>j的序列,也就是不满足条件的序列,适当地调整左端点pos[j],使其满足cnt[j]≤j。其中对于左端点的调整只需要暴力即可,这是均摊O(1)的。
所以这道题最终的时间复杂度为O(sqrt(n)n)。
这题n2的暴力非常好想,预处理[l,r]有多少种食物sum(l,r),然后f(i)=min{f(j)+sum(j+1,i)2}(1<=j<i)
然后利用一点数学知识就有一个非常巧妙的优化
这道题的答案是不会超过n的,所以要想最优的话,枚举的sum(l,r)不能超过n√
预处理pre(i),nxt(i)表示与位置i食物相同的前一个下一个的位置
pos(j)表示[pos(j)+1,i]一共有j种不同的食物
那么f(i)=min{f(pos(j))+j∗j}(0<=j<=n√)
如何维护pos(j)呢?
记cnt(j)表示[pos(j),i]一共有多少种颜色
当i从i-1转移来时可以通过判断位置i的食物的pre来计算cnt(j)
如果cnt(j)>j即不合法,那么pos(j)要向后移动,直到把某一种颜色在区间中完全删除,这中间可以用nxt来判断
pos(j)单调移动,总时间复杂度O(nn√)
#include<bits/stdc++.h> using namespace std; const int M = 4e4 + 10; int a[M], dp[M], lst[M], cnt[M], n, m, pos[M]; int read(){ int x = 0; int f = 1; char c = getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();} return x*=f; } int main(){ freopen("cleanup.in","r",stdin); freopen("cleanup.out","w",stdout); n = read(), m = read(); int blo = sqrt(n); memset(dp, 127, sizeof(dp)); dp[0] = 0; for(int i = 1; i <= n; i++){ a[i] = read(); for(int j = 1; j <= blo; j++) if(lst[a[i]] <= pos[j]) cnt[j]++; lst[a[i]] = i; for(int j = 1; j <= blo; j++) if(cnt[j] > j){ int t = pos[j] + 1; while(lst[a[t]] > t)t++; pos[j] = t; cnt[j]--; } for(int j = 1; j <= blo; j++) dp[i] = min(dp[i], dp[pos[j]] + j * j); } printf("%d ",dp[n]); }
T2:
显然是二分+判断可行性.
如何判断可行性呢?我们注意到是3个正方形.为什么是3个?
我们先找出覆盖所有点的最小距形,那么距形的四条边必须有正方形贴着,而又是3个正方形,所以至少要有1个正方形同时贴着两条边.
1.贴着的边相对
这样的话三个正方形都同时贴着相对的两条边,比如是上下两条边,那么贴着左边的那个正方形就贴着3条边,在距形的一角.
2.贴着的边相邻
这样的话这个正方形就在距形的一角.
所以我们递归的把正方形放在距形的一角,然后去掉已经覆盖了的点,继续放正方形即可.
#include<bits/stdc++.h> using namespace std; const int M = 4e4 + 10, inf = 2e9; #define RG register struct Mat{ int x, y; }f[4][M], p[M], tmp[M]; inline int max(int a, int b){return a >= b ? a : b;} inline int min(int a, int b){return a <= b ? a : b;} bool check(int k, int upx, int dwx, int lfy, int rgy, int res, int now){ if(!now)return 1; if(res == 1) return max((upx - dwx), (rgy - lfy)) <= k; else { //zuoxia; for(int i = 1; i <= now; i++) f[res][i] = tmp[i]; int nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf; int zy = lfy, ry = lfy + k, zx = dwx, rx = dwx + k, cnt = 0; for(RG int i = 1; i <= now; i++){ if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){ nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x); nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y); tmp[++cnt] = f[res][i]; } } if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)){ //printf("%d 1 ",res); return 1; } nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf; zy = lfy, ry = lfy + k, zx = upx-k, rx = upx, cnt = 0; for(RG int i = 1; i <= now; i++){ if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){ nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x); nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y); tmp[++cnt] = f[res][i]; } } if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)) { //printf("%d 2 ",res); return 1; } nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf; zy = rgy-k, ry = rgy, zx = dwx, rx = dwx + k, cnt = 0; for(RG int i = 1; i <= now; i++){ if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){ nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x); nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y); tmp[++cnt] = f[res][i]; } } if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)){ // printf("%d 3 ",res); return 1; } nupx = -inf, ndwx = inf, nlfy = inf, nrgy = -inf; zy = rgy - k, ry = rgy, zx = upx - k, rx = upx, cnt = 0; for(RG int i = 1; i <= now; i++){ if(f[res][i].x < zx || f[res][i].y < zy || f[res][i].x > rx || f[res][i].y > ry){ nupx = max(nupx, f[res][i].x), ndwx = min(ndwx, f[res][i].x); nlfy = min(nlfy, f[res][i].y), nrgy = max(nrgy, f[res][i].y); tmp[++cnt] = f[res][i]; } } if(check(k, nupx, ndwx, nlfy, nrgy, res-1, cnt)) { // printf("%d 4 ",res); return 1; } return 0; } } int read(){ int x = 0; int f = 1; char c = getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();} return x*=f; } int main(){ freopen("cover.in","r",stdin); freopen("cover.out","w",stdout); int n = read(); int upx = -inf, dwx = inf, lfy = inf, rgy = -inf; for(int i = 1; i <= n; i++){ p[i].x = read(), p[i].y = read(); tmp[i] = p[i]; upx = max(upx, p[i].x), dwx = min(dwx, p[i].x); lfy = min(lfy, p[i].y), rgy = max(rgy, p[i].y); } //sort(f + 1, f + 1 + n); int L = 1, R = max(upx - dwx, rgy - lfy), ans = -1; while(L <= R){ for(int i = 1; i <= n; i++) tmp[i] = p[i]; int mid = (L + R) >> 1; if(check(mid, upx, dwx, lfy, rgy, 3, n)) ans = mid, R = mid - 1; else L = mid + 1; } printf("%d ", ans); }
T3:
我们发现第N个收费站是没有用的。。然后在操作时将r--简化操作。首先那个期望是很坑爹的。实际上,期望可以用总和/总状态数解决,我们不妨设a<b,这样期望不变(因为a≠b),从而总状态数即(r-l+2)*(r-l+1)/2。然后考虑总和,我们对每一个收费站单独分析。对于收费站i,它对总和的贡献为v[i]*(i-l+1)*(r-i+1),可以发现(i-l+1)*(r-i+1)即经过i的路径的条数。因此查询l,r的总和即Σ(i=l,r) v[i]*(i-l+1)*(r-i+1)。
然后就变成了区间修改区间查询的经典线段树题目了(不过好像并没有什么转化)
但是直接维护答案(或者用几个辅助数组)是会写疯掉的。。(←亲身体验)。我们把查询的那个式子展开,然后以i为主元降幂排列把那个式子化成:
Σ(i=l,r) -i^2 *a[i]+i*a[i]*(l+r)-(r+1)*(l-1)*a[i]=-Σ(i=l,r)-i^2 *a[i]+(l+r)Σ(i=1,r)i*a[i]-(r+1)*(l-1)Σ(i=l,r)a[i]
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<queue> #include<vector> #include<set> #define for2(a,b,i) for(int i=a;i>=b;--i) #define for1(a,b,i) for(int i=a;i<=b;++i) #define maxn 100005 using namespace std; typedef long long ll; inline ll read(){ char x=getchar();ll f=1,sum=0; while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();} while(x>='0'&&x<='9'){sum=sum*10+x-'0';x=getchar();} return f*sum; } int n,m; ll ans2,ans1,ans,he[maxn]; char kk[2]; inline ll gcd(ll x,ll y){ if(!y) return x; return gcd(y,x%y); } ll sum2[maxn*4],sum1[maxn*4],sum[maxn*4],data[maxn*4]; inline void mainten(int root,int l,int r,ll zhi){ data[root]+=zhi; sum2[root]+=(he[r]-he[l-1])*zhi; sum1[root]+=(ll)(l+r)*(r-l+1)/2*zhi; sum[root]+=(ll)(r-l+1)*zhi; } inline void updata(int root,int l,int r){ int mid=(l+r)>>1; mainten(root<<1,l,mid,data[root]); mainten(root<<1|1,mid+1,r,data[root]); data[root]=0; } inline void updata2(int root){ sum2[root]=sum2[root<<1]+sum2[root<<1|1]; sum1[root]=sum1[root<<1]+sum1[root<<1|1]; sum[root]=sum[root<<1]+sum[root<<1|1]; } inline void change(int lx,int rx,int root,int l,int r,ll zhi){ if(lx<=l&&rx>=r){mainten(root,l,r,zhi);return;} int mid=(l+r)>>1; if(data[root]) updata(root,l,r); if(lx<=mid) change(lx,rx,root<<1,l,mid,zhi); if(rx>mid) change(lx,rx,root<<1|1,mid+1,r,zhi); updata2(root); } inline void query(int lx,int rx,int root,int l,int r){ if(lx<=l&&rx>=r){ ans2+=sum2[root]; ans1+=sum1[root]; ans+=sum[root]; return; } int mid=(l+r)>>1; if(data[root]) updata(root,l,r); if(lx<=mid) query(lx,rx,root<<1,l,mid); if(rx>mid) query(lx,rx,root<<1|1,mid+1,r); updata2(root); } inline void change(){ ll x=read(),y=read(),z=read(); change(x,y-1,1,1,n,z); } inline void query(){ ll x=read(),y=read(); ll oo=0; ans2=ans1=ans=0; ll di=(ll)(y-x+1)*(y-x)>>1; query(x,y-1,1,1,n); oo=(ll)(y+x-1)*ans1+(ll)(-y*x+y)*ans-ans2; ll GCD=gcd(oo,di); printf("%lld/%lld ",oo/GCD,di/GCD); } int main(){ freopen("roadxw.in","r",stdin); freopen("roadxw.out","w",stdout); n=read(),m=read(); for1(1,n,i) he[i]=he[i-1]+(ll)i*i; while(m--){ scanf("%s",&kk); if(kk[0]=='C') change(); else query(); } }
还用一种方法,用线段树维护区间的各种和,前缀和的和,后缀和的和,考虑线段树合并时每次跨过中间的贡献,是等差数列形式;
然后我们就只需要维护i*i*a[i],i*a[i],a[i]的区间和了。线段树轻松胜任。最后输出的时候再转化一下就好了。
#include<cstring> #include<algorithm> #include<iostream> #include<cstdio> using namespace std; const int N=1e5+5; struct node{ long long lsum,rsum,an,sum; long long fl; }f[N*4]; long long b[N],c[N]; inline int read() { int ans=0,t=1; char x=getchar(); while(x<'0'||x>'9') { if(x=='-') t=-1; x=getchar(); } while(x>='0'&&x<='9') { ans=ans*10+x-'0'; x=getchar(); } return ans*t; } node up(node ls,node rs,long long llen,long long rlen) { node ans; ans.sum=ls.sum+rs.sum; ans.lsum=ls.lsum+rs.lsum+rlen*ls.sum; ans.rsum=rs.rsum+ls.rsum+llen*rs.sum; ans.an=ls.an+rs.an+ls.rsum*rlen+rs.lsum*llen; ans.fl=0; return ans; } void push_down(int o,int l,int r) { if(f[o].fl!=0) { int ls=o<<1; int rs=o<<1|1; int mid=(r+l)>>1; long long llen=(mid-l+1); long long rlen=(r-mid); f[ls].sum+=llen*f[o].fl; f[rs].sum+=rlen*f[o].fl; f[ls].lsum+=(c[llen]*f[o].fl); f[ls].rsum+=(c[llen]*f[o].fl); f[rs].lsum+=(c[rlen]*f[o].fl); f[rs].rsum+=(c[rlen]*f[o].fl); f[ls].an+=(b[llen]*f[o].fl); f[rs].an+=(b[rlen]*f[o].fl); f[ls].fl+=f[o].fl; f[rs].fl+=f[o].fl; f[o].fl=0; } } void modi(int o,int l,int r,const int L,const int R,long long data) { if(L<=l&&R>=r) { long long len=(r-l+1); f[o].rsum+=(c[len]*data); f[o].lsum+=(c[len]*data); f[o].sum+=data*len; f[o].an+=(b[len]*data); f[o].fl+=data; return ; } int mid=(l+r)>>1; int ls=o<<1; int rs=o<<1|1; push_down(o,l,r); if(L<=mid) modi(ls,l,mid,L,R,data); if(R>mid) modi(rs,mid+1,r,L,R,data); f[o]=up(f[ls],f[rs],mid-l+1,r-mid); } node query(int o,int l,int r,int L,int R) { if(L<=l&&R>=r) { return f[o]; } int mid=(l+r)>>1; push_down(o,l,r); int ls=o<<1; int rs=o<<1|1; if(L>mid) return query(rs,mid+1,r,L,R); if(R<=mid) return query(ls,l,mid,L,R); node lans=query(ls,l,mid,L,mid); node rans=query(rs,mid+1,r,mid+1,R); long long llen=mid-L+1,rlen=R-mid; node ans; ans.sum=lans.sum+rans.sum; ans.lsum=lans.lsum+rans.lsum+rlen*lans.sum; ans.rsum=rans.rsum+lans.rsum+llen*rans.sum; ans.an=lans.an+rans.an+lans.rsum*rlen+rans.lsum*llen; ans.fl=0; return ans; } inline long long gcd(long long a,long long b) { if(b==0) return a; return gcd(b,a%b); } char s1[5]; int main() { freopen("roadxw.in","r",stdin); freopen("roadxw.out","w",stdout); int n,m; n=read(); m=read(); for(long long i=1;i<=n;i++) b[i]=i*(i+1)/2; for(int i=1;i<=n;i++) b[i]+=b[i-1]; for(long long i=1;i<=n;i++) c[i]=i*(i+1)/2; int len=n-1; for(int i=1;i<=m;i++) { scanf("%s",s1); if(s1[0]=='C'){ int x,y,z; x=read(); y=read(); y--; z=read(); modi(1,1,len,x,y,z); } if(s1[0]=='Q') { int x,y; x=read(); y=read(); y--; long long mu=1ll*(y-x+1)*(y-x+2)/2; node ans=query(1,1,len,x,y); long long zi=ans.an; long long g=gcd(mu,zi); printf("%lld/%lld ",zi/g,mu/g); } } return 0; }