contest-20191021
文化课读的真不开心
回来竞赛
假人
sol
根据不等式有 abs(a-b)+abs(b-c)>=abs(a-c)
那么每一个都会选。
可以发现每一段只会选在端点上(否则移到端点更优)。
那么dp f[i][0/1]表示前i个选在上/下端点的最大值。
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100005 using namespace std; int n,a[maxn][2]; long long s1,s2,f[maxn][2]; int main() { freopen("arachne.in","r",stdin); freopen("arachne.out","w",stdout); cin>>n; for(int i=1;i<=n;i++)scanf("%d%d",&a[i][0],&a[i][1]); for(int i=2;i<=n;i++){ s1=max(abs(a[i][0]-a[i-1][1])+f[i-1][1],abs(a[i][0]-a[i-1][0])+f[i-1][0]); s2=max(abs(a[i][1]-a[i-1][1])+f[i-1][1],abs(a[i][1]-a[i-1][0])+f[i-1][0]); f[i][0]=s1;f[i][1]=s2; } cout<<max(f[n][0],f[n][1])<<endl; return 0; }
愚者
sol
考虑把点按权值大到小加入,用并查集维护联通快大小,那么每次加入的一定是最小值。
把式子展开,发现它形如y=kx+b.
每次得到一组(k,b)
对这些直线构出下凸壳,线性处理ans
100%数据范围小于部分分范围是什么鬼,差评投诉
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 300005 #define ll long long using namespace std; int n,m,Q,head[maxn],tot; int f[maxn],sz[maxn],flag[maxn]; ll ans[maxn]; struct line{ ll a,b; }s[maxn],li[maxn],l[maxn],q[maxn]; struct node{ int v,nex; }e[maxn]; void add(int t1,int t2){ e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot; } bool cmp(line A,line B){return A.a>B.a;} bool dmp(line A,line B){return A.a<B.a||(A.a==B.a&&A.b<B.b);} int getf(int k){return f[k]==k?k:f[k]=getf(f[k]);} double C(line A,line B){ return (double)(B.b-A.b)/(double)(A.a-B.a); } int main() { cin>>n>>m>>Q; for(int i=1;i<=n;i++){ scanf("%lld",&s[i].a),s[i].b=i; f[i]=i;sz[i]=1; } for(int i=1,t1,t2;i<=m;i++){ scanf("%d%d",&t1,&t2); add(t1,t2);add(t2,t1); } for(int i=1;i<=Q;i++)scanf("%lld",&q[i].a),q[i].b=i; sort(s+1,s+n+1,cmp); for(int x=1;x<=n;x++){ flag[s[x].b]=1; for(int i=head[s[x].b];i;i=e[i].nex){ if(flag[e[i].v]){ int fv=getf(e[i].v); if(fv!=s[x].b)f[fv]=s[x].b,sz[s[x].b]+=sz[fv]; } } li[x].a=sz[s[x].b];li[x].b=s[x].a*sz[s[x].b]; } sort(li+1,li+n+1,dmp);sort(q+1,q+Q+1,dmp); int j=1; int tp=0; for(int i=1;i<=n;i++){ if(li[i].a==li[i+1].a&&li[i].b<=li[i+1].b)continue; while(tp>1&&C(l[tp-1],l[tp])>C(l[tp-1],li[i]))tp--; l[++tp]=li[i]; } for(int i=1;i<=tp;i++){ double cr; if(i<tp)cr=C(l[i],l[i+1]); else cr=1e7; for(;j<=Q&&(double)q[j].a<cr;j++)ans[q[j].b]=l[i].a*q[j].a+l[i].b; } for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]); return 0; }
拙者
sol
40% 贪心从前往后再从后往前取即可
20% (1,2) 线段树维护最小值
100%
考虑第一次从左往右的修改。
把+1看成向上,-1看成向下,这个序列看成折现
每一个点的最终位置是他减他之前的最小值,也就是把他之前的最小值提到0(水平位置)。
那么考虑维护区间最大值,最小值,极差,总和。
从前往后是答案是 max(-最小值,0)
从前往后是max(极差-(sum-Min))
极差就是最高点,sum-Min为最后一个点的位置。
60%
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 200005 #define mid ((l+r)>>1) #define ls k<<1 #define rs k<<1|1 using namespace std; int n,q,a[maxn],b[maxn]; char ch[maxn]; struct node{ int v,bj; }tr[maxn*4]; node wh(node A,node B){ node C; C.sum=A.sum+B.sum; C.mi=min(A.mi,A.sum+B.mi); C.ma=max(A.ma,A.sum+B.ma); C.d=max(max(A.d,B.d),A.sum+B.ma-A.mi); return C; } void build(int k,int l,int r){ if(l==r){tr[k].sum=tr[k].ma=tr[k].mi=a[l];tr[k].d=0;return;} build(ls,l,mid);build(rs,mid+1,r); tr[k]=wh(tr[ls],tr[rs]); } void upd(int k,int l,int r,int pl){ if(l==r){ tr[k].sum=tr[k].ma=tr[k].mi=a[l]; return; } if(pl<=mid)upd(ls,l,mid,pl); else upd(rs,mid+1,r,pl); tr[k]=wh(tr[ls],tr[rs]); } node ask(int k,int l,int r,int li,int ri){ if(l>=li&&r<=ri)return tr[k]; if(ri<=mid)return ask(ls,l,mid,li,ri); if(li>mid)return ask(rs,mid+1,r,li,ri); return wh(ask(ls,l,mid,li,ri),ask(rs,mid+1,r,li,ri)); } void wh(int k){ tr[k].v=min(tr[ls].v,tr[rs].v); } void build(int k,int l,int r){ if(l==r){tr[k].v=b[l];return;} build(ls,l,mid);build(rs,mid+1,r); wh(k); } void upd(int k,int v){ tr[k].bj+=v;tr[k].v+=v; } void down(int k){ if(tr[k].bj!=0){ upd(ls,tr[k].bj);upd(rs,tr[k].bj); tr[k].bj=0; } } void add(int k,int l,int r,int li,int ri,int v){ if(l>=li&&r<=ri){ upd(k,v);return; } down(k); if(li<=mid)add(ls,l,mid,li,ri,v); if(ri>mid)add(rs,mid+1,r,li,ri,v); wh(k); } int ask(int k,int l,int r,int li,int ri){ if(li==0)return 0; if(l>=li&&r<=ri)return tr[k].v; down(k); int Min=1e9; if(li<=mid)Min=min(Min,ask(ls,l,mid,li,ri)); if(ri>mid)Min=min(Min,ask(rs,mid+1,r,li,ri)); return Min; } int main() { cin>>n;scanf(" %s",ch+1); for(int i=1;i<=n;i++){ a[i]=(ch[i]=='(')?1:-1; } if(n<=1000){ scanf("%d",&q); for(int I=1,op,l,r;I<=q;I++){ scanf("%d",&op); if(op==1)scanf("%d",&l),a[l]=-a[l]; else { scanf("%d%d",&l,&r); if(op==2){ int tmp=0,ans=0; for(int i=l;i<=r;i++){ b[i]=a[i]; if(tmp+b[i]<0)b[i]=0,ans++; tmp+=b[i]; } printf("%d\n",ans); } else { int tmp=0,ans=0; for(int i=l;i<=r;i++){ b[i]=a[i]; if(tmp+b[i]<0)b[i]=0,ans++; tmp+=b[i]; } tmp=0; for(int i=r;i>=l;i--){ if(tmp+b[i]<0)b[i]=0,ans++; tmp+=b[i]; } printf("%d\n",ans); } } } } else { for(int i=1;i<=n;i++)b[i]=b[i-1]+a[i]; build(1,1,n); scanf("%d",&q); for(int I=1,op,l,r;I<=q;I++){ scanf("%d",&op); if(op==1){ scanf("%d",&l),add(1,1,n,l,n,-2*a[l]); a[l]=-a[l]; } else { scanf("%d%d",&l,&r); if(op==2){ int x=ask(1,1,n,l-1,l-1),y=ask(1,1,n,l,r); printf("%d\n",max(x-y,0)); } } } } return 0; }
100%
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define mid ((l+r)>>1) #define ls k<<1 #define rs k<<1|1 #define maxn 200005 using namespace std; int n,q,a[maxn]; char ch[maxn]; struct node{ int ma,mi,sum,d; }tr[maxn*4]; node wh(node A,node B){ node C; C.sum=A.sum+B.sum; C.mi=min(A.mi,A.sum+B.mi); C.ma=max(A.ma,A.sum+B.ma); C.d=max(max(A.d,B.d),A.sum+B.ma-A.mi); return C; } void build(int k,int l,int r){ if(l==r){tr[k].sum=a[l];tr[k].ma=max(a[l],0);tr[k].mi=min(a[l],0);tr[k].d=0;return;} build(ls,l,mid);build(rs,mid+1,r); tr[k]=wh(tr[ls],tr[rs]); } void upd(int k,int l,int r,int pl){ if(l==r){ tr[k].sum=a[l];tr[k].ma=max(a[l],0);tr[k].mi=min(a[l],0);tr[k].d=0; tr[k].d=0; return; } if(pl<=mid)upd(ls,l,mid,pl); else upd(rs,mid+1,r,pl); tr[k]=wh(tr[ls],tr[rs]); } node ask(int k,int l,int r,int li,int ri){ if(l>=li&&r<=ri)return tr[k]; if(ri<=mid)return ask(ls,l,mid,li,ri); if(li>mid)return ask(rs,mid+1,r,li,ri); return wh(ask(ls,l,mid,li,ri),ask(rs,mid+1,r,li,ri)); } int main() { cin>>n;scanf(" %s",ch+1); for(int i=1;i<=n;i++){ a[i]=(ch[i]=='(')?1:-1; } build(1,1,n); scanf("%d",&q);int tp=0; for(int I=1,op,l,r;I<=q;I++){ scanf("%d",&op); if(op==1){ scanf("%d",&l);a[l]=-a[l]; upd(1,1,n,l); } else { scanf("%d%d",&l,&r); if(op==2){ node ans=ask(1,1,n,l,r); printf("%d\n",max(-ans.mi,0)); } else { node ans=ask(1,1,n,l,r); int sum=max(-ans.mi,0)+max(ans.d-(ans.sum-ans.mi),0); printf("%d\n",sum); } } } return 0; } /* 4 (()) 1 3 1 4 */
注意事项:
1.若查询l-1要特判0
2.修改a[l]外面也要改
3.区间最小值、最大值都要和0取,因为基准是0