比赛链接:https://codeforces.com/contest/1557
一小时做完了A,B,C,剩下一小时想D,也没想出来。不过最终是500多名,上了一百多分,真舒适。因为是和排名有关的计分,所以做题速度很重要啊。
A
分析:
当时感觉是直接让最大数一组,剩下的另一组,写了就过了;
详细证明 Tutorial 写了。
代码如下:
#include<iostream> #include<algorithm> #define db double using namespace std; int const N=1e5+5; int T,n; int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); db sum=0; int mx=-1e9-1; for(int i=1,x;i<=n;i++) { scanf("%d",&x); sum+=x; if(x>mx)mx=x; } printf("%lf ",mx+(sum-mx)/(n-1)); } return 0; }
B
分析:
离散化一下,找最多的连续上升子段的个数,小于等于( k )就可以,因为它们内部可以随便再分。
代码如下:
#include<iostream> #include<cstring> #include<algorithm> using namespace std; int const N=1e5+5; int T,n,k,a[N],b[N]; int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+n+1); for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+n+1,a[i])-b; int cnt=1; for(int i=2;i<=n;i++) { if(a[i]==a[i-1]+1)continue; else cnt++; } if(cnt<=k)puts("Yes"); else puts("No"); } return 0; }
C
分析:
如果( n )是奇数,那么( & )为( 1 )的位上( igoplus )也是( 1 );所以要枚举( k )个位置上一共有几个( 1 ),分别在哪儿,然后让其他位上都是( 0 )。
如果( n )是偶数,那么( & )为( 1 )的位上( igoplus )是( 0 );所以枚举哪个位置出现第一个( 1 ),让前面的位都是( 0 ),后面的位随便取;还要加上没有( 1 )的方案数。
让一个位保证是( 0 ),也就是找到( < n )的偶数个数,让它们在该位取( 1 ),其他数在该位取( 0 )。
懒得写公式了所以用文字描述了半天;公式看代码即知。
代码如下:
#include<iostream> #define ll long long using namespace std; int const N=2e5+5,md=1e9+7; int T,n,k; ll cc,ans,fc[N],inv[N]; ll pw(ll x,ll y) { ll ret=1,a=x%md; while(y) { if(y&1)ret=ret*a%md; a=a*a%md; y>>=1; } return ret; } ll C(int a,int b){return a<b?0:((fc[a]*inv[b])%md*inv[a-b])%md;} void init() { cc=0; ans=0; for(int i=0;i<n;i+=2)cc=(cc+C(n,i))%md; } int main() { scanf("%d",&T); fc[0]=1; for(int i=1;i<N;i++)fc[i]=(fc[i-1]*i)%md; inv[N-1]=pw(fc[N-1],md-2); for(int i=N-2;i>=0;i--)inv[i]=(inv[i+1]*(i+1))%md; while(T--) { scanf("%d%d",&n,&k); init(); if(k==0){printf("1 "); continue;} if(n%2) { for(int i=0;i<=k;i++) ans=(ans+C(k,i)*pw(cc,k-i)%md)%md; } else { ans=pw(cc,k); for(int i=1;i<=k;i++) ans=(ans+pw(cc,k-i)*pw(pw(2,n),i-1)%md)%md; } printf("%lld ",ans); } return 0; }
D
分析:
比赛时想了想线段树、DP,但没有成形的想法。后来做别的去了。
这题确实是DP,关键是每一行要找到一行来更新它。开一个线段树,每个点存了一个(val)和一个(id),表示在这一点为(1),最大能保留多少行,最后一行是第几行。对于第(i)行,找线段树上它是(1)的所有区间中(val)的最大值,然后把它继承下来,线段树上它所有区间更新成(val=maxleft { val,mx.val+1 ight });若成功更新,对应区间的(id=i)。
过程中记录(pre[i]),表示更新第(i)行的是哪一行。再记录最大的(val)以及它出现在哪一行。最后就可以找到最大(val)对应的路径,然后输出答案了。
代码如下:
#include<iostream> #include<algorithm> #include<vector> #include<cstring> #define mp make_pair #define fst first #define scd second #define pb push_back #define ls (u<<1) #define rs ((u<<1)|1) #define mid ((l+r)>>1) using namespace std; int const N=6e5+5; int n,m,c[N],cnt,pre[N],vis[N]; struct Nd{ int val,id; }tr[N<<2],lz[N<<2]; vector<pair<int,int> >qu[N]; void build(int u,int l,int r) { if(l==r){tr[u].val=0; tr[u].id=-1; return;} build(ls,l,mid); build(rs,mid+1,r); } Nd bet(Nd a,Nd b){return (a.val>b.val)?a:b;} void pup(int u){tr[u]=bet(tr[ls],tr[rs]);} void pdn(int u) { if(lz[u].val==0)return; tr[ls]=lz[u]; lz[ls]=lz[u]; tr[rs]=lz[u]; lz[rs]=lz[u]; lz[u]=(Nd){0,-1}; } Nd find(int u,int l,int r,int ql,int qr) { //printf("fd(%d,%d,%d,%d,%d) ",u,l,r,ql,qr); if(l>r)return (Nd){0,-1}; if(l>=ql&&r<=qr)return tr[u]; pdn(u); Nd ret=(Nd){0,-1}; if(mid>=ql)ret=bet(ret,find(ls,l,mid,ql,qr)); if(mid<qr)ret=bet(ret,find(rs,mid+1,r,ql,qr)); return ret; } void upt(int u,int l,int r,int ql,int qr,Nd s) { if(l>r)return; if(l>=ql&&r<=qr) { if(tr[u].val<=s.val)tr[u]=s,lz[u]=s; else if(l==r)return; else { pdn(u); if(tr[ls].val<=s.val)tr[ls]=s,lz[ls]=s; else upt(ls,l,mid,ql,qr,s); if(tr[rs].val<=s.val)tr[rs]=s,lz[rs]=s; else upt(rs,mid+1,r,ql,qr,s); pup(u); } return; } pdn(u); if(mid>=ql)upt(ls,l,mid,ql,qr,s); if(mid<qr)upt(rs,mid+1,r,ql,qr,s); pup(u); return; } int main() { scanf("%d%d",&n,&m); for(int i=1,t,l,r;i<=m;i++) { scanf("%d%d%d",&t,&l,&r); qu[t].pb(mp(l,r)); c[++cnt]=l; c[++cnt]=r; } sort(c+1,c+cnt+1); cnt=unique(c+1,c+cnt+1)-c-1; build(1,1,cnt); int ans=0,row=-1; memset(pre,-1,sizeof pre); for(int i=1;i<=n;i++) { Nd mx=(Nd){0,-1}; for(pair<int,int> it:qu[i]) { int ql=lower_bound(c+1,c+cnt+1,it.fst)-c,qr=lower_bound(c+1,c+cnt+1,it.scd)-c; mx=bet(mx,find(1,1,cnt,ql,qr)); } //printf("i=%d mx.val=%d mx.id=%d ",i,mx.val,mx.id); pre[i]=mx.id; Nd nw=(Nd){mx.val+1,i}; if(nw.val>ans)ans=nw.val,row=i; for(pair<int,int> it:qu[i]) { int ql=lower_bound(c+1,c+cnt+1,it.fst)-c,qr=lower_bound(c+1,c+cnt+1,it.scd)-c; upt(1,1,cnt,ql,qr,nw); } } printf("%d ",n-ans); while(row>-1)vis[row]=1,row=pre[row]; for(int i=1;i<=n;i++) if(!vis[i])printf("%d ",i); return 0; }