开题1小时(雾)严重影响我的提交以及做题心情。。我刚开题就发现有人阿克了。。
实际上这场div3真心简单良心很休闲。
A:送分题,先排序,每次枚举一下这个数可以加到哪个集合里,加进去就行。

1 #include<stdio.h>
2 #include<algorithm>
3 #define it register int
4 #define il inline
5 using namespace std; 6 const int N=1000005; 7 int a[N],o[N],n,ans,T; 8 il void fr(int &num){ 9 num=0;char c=getchar();int p=1; 10 while(c<'0'||c>'9') c=='-'?p=-1,c=getchar():c=getchar(); 11 while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar(); 12 num*=p; 13 } 14 int main(){ 15 fr(T); 16 while(T--){ 17 fr(n),ans=0; 18 for(it i=1;i<=n;++i) fr(a[i]); 19 sort(a+1,a+1+n); 20 for(it i=1,fl;i<=n;++i){ 21 fl=1; 22 for(it j=1;j<=ans;++j) 23 if(a[i]-o[j]>1) o[j]=a[i],fl=0,j=ans+1; 24 if(fl) o[++ans]=a[i]; 25 } 26 printf("%d\n",ans); 27 } 28 return 0; 29 }
B:双倍经验送分题。直接跑tarjan,输出每个强连通分量的大小就可以了。注意多测清空。

1 #include<stdio.h>
2 #include<algorithm>
3 #define it register int
4 #define il inline
5 using namespace std; 6 const int N=1000005; 7 int h[N],o[N],n,ans,T,u,v,nxt[N],adj[N],sz[N],dfn[N],low[N],ti,i,s[N],t,top,tot; 8 bool ins[N]; 9 il void fr(int &num){ 10 num=0;char c=getchar();int p=1; 11 while(c<'0'||c>'9') c=='-'?p=-1,c=getchar():c=getchar(); 12 while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar(); 13 num*=p; 14 } 15 il void add(){ 16 nxt[++t]=h[u],h[u]=t,adj[t]=v; 17 } 18 il int Min(it p,it q){ 19 return p<q?p:q; 20 } 21 il void scc(it x){ 22 dfn[x]=low[x]=++ti,s[++top]=x,ins[x]=true; 23 for(it i=h[x],j;i;i=nxt[i]) !dfn[j=adj[i]]?scc(j),low[x]=Min(low[x],low[j]):low[x]=Min(low[x],ins[j]?dfn[j]:low[x]); 24 if(dfn[x]==low[x]){ 25 it v;++tot; 26 do{ 27 v=s[top--],ins[v]=false,o[v]=tot,++sz[tot]; 28 }while(x!=v&&top); 29 } 30 } 31 int main(){ 32 fr(T); 33 while(T--){ 34 fr(n); 35 for(u=1;u<=n;++u) fr(v),add(); 36 for(it i=1;i<=n;++i) if(!dfn[i]) scc(i); 37 for(it i=1;i<=n;++i) printf("%d ",sz[o[i]]); 38 putchar('\n'); 39 for(it i=1;i<=n;++i) dfn[i]=low[i]=o[i]=sz[i]=h[i]=ins[i]=s[i]=0; 40 t=top=ti=tot=0; 41 } 42 return 0; 43 }
C:双倍经验送分题。很显然>=n的最大的只要把n里面所有的3的幂去除,然后把剩下未选中的挑一个最小的且比去除之后的n大的加入答案就行。注意到样例里面给的最大的那个是3^38.

1 #include<stdio.h> 2 #include<queue> 3 #include<algorithm> 4 #define it register int 5 #define il inline 6 using namespace std; 7 const int N=1000005; 8 typedef long long ll; 9 ll pw[N],ans,n,T; 10 bool inq[N]; 11 il void fr(ll &num){ 12 num=0;char c=getchar();int p=1; 13 while(c<'0'||c>'9') c=='-'?p=-1,c=getchar():c=getchar(); 14 while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar(); 15 num*=p; 16 } 17 int main(){ 18 pw[0]=1ll;for(it i=1;i<=38;++i) pw[i]=pw[i-1]*3; 19 fr(T); 20 while(T--){ 21 fr(n),ans=0; 22 for(it i=0;i<=38;++i) inq[i]=0; 23 for(it i=38;i>=0;--i) if(n>pw[i]) n-=pw[i],ans+=pw[i],inq[i]=1; 24 for(it i=0;i<=38;++i) if(inq[i]) ans-=pw[i]; else{ans+=pw[i];break;} 25 printf("%I64d\n",ans); 26 } 27 return 0; 28 }
D:双倍经验,有点难度。考场降智,先打了一个树状数组,用小号交,一发WA了,二发WA了。赶紧换个思路,写了线段树。唯一需要注意的地方就是先排序,先排r再排l,从小到大。

1 #include<stdio.h>
2 #include<algorithm>
3 #define it register int
4 #define il inline
5 using namespace std; 6 const int N=1000005; 7 int o[N],ans,s[N],add[N],u,v,x,cnt,k,n,maxn; 8 il int Max(it p,it q){ 9 return p>q?p:q; 10 } 11 struct ky{ 12 int l,r,id; 13 bool operator<(const ky&p)const{ 14 return r^p.r?r<p.r:l<p.l; 15 } 16 }a[N]; 17 il void fr(int &num){ 18 num=0;char c=getchar();int p=1; 19 while(c<'0'||c>'9') c=='-'?p=-1,c=getchar():c=getchar(); 20 while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar(); 21 num*=p; 22 } 23 il void pd(it l,it r,it rt){ 24 it mid=l+r>>1; 25 add[rt<<1]+=add[rt],add[rt<<1|1]+=add[rt],s[rt<<1]+=add[rt],s[rt<<1|1]+=add[rt],add[rt]=0; 26 } 27 il void upd(it l,it r,it rt){ 28 if(l>v||r<u) return; 29 if(l>=u&&r<=v){ 30 s[rt]+=x,add[rt]+=x;return; 31 } 32 if(add[rt]) pd(l,r,rt); 33 it mid=l+r>>1; 34 upd(l,mid,rt<<1),upd(mid+1,r,rt<<1|1),s[rt]=Max(s[rt<<1],s[rt<<1|1]); 35 } 36 il void q(it l,it r,it rt){ 37 if(l>v||r<u) return; 38 if(l>=u&&r<=v){ 39 ans=Max(ans,s[rt]);return; 40 } 41 if(add[rt]) pd(l,r,rt); 42 it mid=l+r>>1; 43 q(l,mid,rt<<1),q(mid+1,r,rt<<1|1),s[rt]=Max(s[rt<<1],s[rt<<1|1]); 44 } 45 int main(){ 46 fr(n),fr(k); 47 for(it i=1;i<=n;++i) fr(a[i].l),fr(a[i].r),a[i].id=i,maxn=(a[i].r>maxn?a[i].r:maxn); 48 sort(a+1,a+1+n);x=1; ; 49 for(it i=1;i<=n;++i){ 50 u=a[i].l,v=a[i].r,ans=0; 51 q(1,maxn,1); 52 ans<k?upd(1,maxn,1),0:o[++cnt]=a[i].id; 53 } 54 printf("%d\n",cnt); 55 sort(o+1,o+1+cnt); 56 for(it i=1;i<=cnt;++i) printf("%d ",o[i]); 57 return 0; 58 }
E:一道弱智dp,比D还简单。出题人应该在E的tag上加一个“开题顺序”。设f[i][0],f[i][1]表示选择楼梯和电梯的状态,直接按照题意模拟就行。

1 #include<stdio.h>
2 #define it register int
3 #define il inline
4 const int N=1000005; 5 il void fr(int &num){ 6 num=0;char c=getchar();int p=1; 7 while(c<'0'||c>'9') c=='-'?p=-1,c=getchar():c=getchar(); 8 while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar(); 9 num*=p; 10 } 11 il int Min(it p,it q){ 12 return p<q?p:q; 13 } 14 int a[N],b[N],f[N][2],n,c; 15 int main(){ 16 fr(n),fr(c);--n; 17 for(it i=1;i<=n;++i) fr(a[i]); 18 for(it i=1;i<=n;++i) fr(b[i]); 19 f[0][1]=1e9; 20 for(it i=1;i<=n;++i) f[i][0]=Min(f[i-1][0],f[i-1][1])+a[i],f[i][1]=Min(f[i-1][0]+c,f[i-1][1])+b[i]; 21 printf("0 ");for(it i=1;i<=n;++i) printf("%d ",Min(f[i][0],f[i][1])); 22 return 0; 23 }
F:比E还傻的一题,n<=200直接暴力找……
我们考虑,假设现在选择节点u,那么我们把和u距离不超过k的节点都不选。但我们不能够保证u就是最优解,于是把和u距离不超过k的节点的价值都减去a[u]。这时候我们再向上寻找,假设某个节点v和u距离不超过k,而a[v]现在值>0(减去a[u]之后),那我们肯定选择a[v]不选择a[u]。于是我们的答案加上现在的a[v],实际上就相当于补上a[u]比a[v]小的那一部分,然后再从v开始Dfs,做和u一样的处理。叶子节点没有孩子满足dp的无后效性,所以肯定是从叶子开始。

1 #include<stdio.h> 2 #include<vector> 3 #define it register int 4 #define il inline 5 using namespace std; 6 const int N=1000005; 7 int n,k,a[N],h[N],nxt[N],adj[N],t,nw,u,v,d[N],ans; 8 vector<int> g[N]; 9 il void add(){ 10 nxt[++t]=h[u],h[u]=t,adj[t]=v,nxt[++t]=h[v],h[v]=t,adj[t]=u; 11 } 12 il void dfs(it x,it fa){ 13 g[d[x]=d[fa]+1].push_back(x);//同一深度放在一起 14 for(it i=h[x],j;i;i=nxt[i]) 15 if((j=adj[i])!=fa) dfs(j,x); 16 } 17 il void fr(int &num){ 18 num=0;char c=getchar();int p=1; 19 while(c<'0'||c>'9') c=='-'?p=-1,c=getchar():c=getchar(); 20 while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar(); 21 num*=p; 22 } 23 il void Dfs(it x,it fa,it dep){ 24 a[x]-=nw; 25 if(dep==k) return;//距离不超过k 26 for(it i=h[x],j;i;i=nxt[i]) 27 if((j=adj[i])!=fa) Dfs(j,x,dep+1); 28 } 29 int main(){ 30 fr(n),fr(k); 31 for(it i=1;i<=n;++i) fr(a[i]); 32 for(it i=1;i<n;++i) fr(u),fr(v),add(); 33 dfs(1,0); 34 for(it i=n;i;--i) 35 for(it j=0,sz=g[i].size(),x;j<sz;++j) 36 if((x=a[g[i][j]])>0) nw=x,ans+=x,Dfs(g[i][j],0,0); 37 printf("%d",ans); 38 return 0; 39 }
看来以后打Div3应该先开EF后开D。。。
今天的比赛应该加个tag“开题顺序”。(吐槽出题人是不是顺序随的看心情的?)
真实难度大约是:A<E<B<F<C<D。。。
赶在官方题解出来之前把自己的题解写完了!开心。完结撒花。
upd:评论区有人对F进行提问,所以新增了一些解释。看不懂的同学欢迎提出问题。