A. Yet Another Dividing into Teams
签到,有相邻的数字 ans=2,否则 ans=1
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
memset(vis,0,sizeof(vis));
for(int i=1,x;i<=n;i++) scanf("%d",&x),vis[x]=1;
int flag=0;
for(int i=1;i<=100;i++) if(vis[i]&&(vis[i-1]||vis[i+1])) {flag=1;break;}
if(flag==0) printf("1\n");
else printf("2\n");
}
return 0;
}
B. Books Exchange
找每个顶点处在的环的大小,dfs 行了
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <utility>
#define MAXN 200010
using namespace std;
const int inf=0x3f3f3f3f;
int ans=inf;
int T,n,to[MAXN],len[MAXN],vis[MAXN];
void dfs(int u,int dis){
if(vis[u]) {len[u]=dis;return;}
vis[u]=1;
dfs(to[u],dis+1);
len[u]=len[to[u]];
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(vis,0,(n+1)*sizeof(int));
memset(len,0,(n+1)*sizeof(int));
for(int i=1;i<=n;i++) scanf("%d",&to[i]);
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
for(int i=1;i<=n;i++) printf("%d ",len[i]);
printf("\n");
}
return 0;
}
C. Good Numbers
我的方法是这样的,先将这个数转化为三进制来看。
从 0 位看到最高位,如果第 i 位是 2, 那么就从 i+1 位到更高的位去找一个是 0 的位 j, 将其变成 1, 然后将 j-1 到 0 位的数字全变成 0.
这样扫描一遍之后, 将这个三进制数转化为十进制就是答案了.

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <utility>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define MAXN 100010
#define mid ((l+r)>>1)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const LL INF=0x3f3f3f3f3f3f3f3f;
int T;
LL n;
int num[100];
int main(){
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
memset(num,0,sizeof(num));
LL k=1,index=0;
for(;k<n;k*=3,index+=1);
while(k){
while(n>=k) n-=k,num[index]++;
k/=3,index-=1;
}
for(int i=0;i<=64;i++){
if(num[i]==2){
for(int j=i+1;j<=64;j++){
if(num[j]==0){
num[j]=1;
for(int k=j-1;k>=0;k--) num[k]=0;
break;
}
}
}
}
LL ans=0;
k=1;
for(int i=0;i<=64;i++) {
if(num[i]) ans+=k;
k*=3;
}
printf("%lld\n",ans);
}
return 0;
}
D. Too Many Segments
我用的贪心+线段树, 结束后发现大神们都用的是优先队列啊, set什么的......
首先我们想要保留尽量多的区间, 那么就要剩下的区间尽量少的重叠, 那么根据这个性质我们可以对区间进行先按右端点从小到大, 若右相等则按左从小到大这样排序.
然后依次插入这些区间, 若一个区间满足这个区间内的点的最大被覆盖次数还不足 k, 那么就将这个区间覆盖下去, 否则这个区间就将被删去, 加入答案. 这个过程需要区间加和查询区间最大值, 用线段树.

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <utility>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define MAXN 200010
#define mid ((l+r)>>1)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const LL INF=0x3f3f3f3f3f3f3f3f;
int n,k;
struct Seg{
int l,r,id;
}p[MAXN];
vector<int> ans;
struct SegmentTree{
int tag[MAXN*4],maxv[MAXN*4];
void build(){memset(tag,0,sizeof(tag));memset(maxv,0,sizeof(maxv));}
void pushdown(int id,int l,int r){
maxv[id<<1]+=tag[id];tag[id<<1]+=tag[id];
maxv[id<<1|1]+=tag[id];tag[id<<1|1]+=tag[id];
tag[id]=0;
}
int update(int id,int l,int r,int L,int R,int x){
if(L<=l&&r<=R){maxv[id]+=x;tag[id]+=x;return 0;}
if(tag[id]) pushdown(id,l,r);
if(L<=mid) update(id<<1,l,mid,L,R,x);
if(R>mid) update(id<<1|1,mid+1,r,L,R,x);
maxv[id]=max(maxv[id<<1],maxv[id<<1|1]);
}
int ask(int id,int l,int r,int L,int R){
if(L<=l&&r<=R) return maxv[id];
if(tag[id]) pushdown(id,l,r);
int res1=0,res2=0;
if(L<=mid) res1=ask(id<<1,l,mid,L,R);
if(R>mid) res2=ask(id<<1|1,mid+1,r,L,R);
return max(res1,res2);
}
}tree;
bool cmp(Seg a,Seg b){
return a.r<b.r||a.r==b.r&&a.l<b.l;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d%d",&p[i].l,&p[i].r),p[i].id=i;
sort(p+1,p+n+1,cmp);
tree.build();
for(int i=1;i<=n;i++){
if(tree.ask(1,1,MAXN,p[i].l,p[i].r)<k) tree.update(1,1,MAXN,p[i].l,p[i].r,1);
else ans.push_back(p[i].id);
}
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++) printf("%d ",ans[i]);
return 0;
}
E. By Elevator or Stairs?
这道题其实比 C 题还要简单一点, 就是一个 dp 水题
走了楼梯再做电梯就要等 c 时间, 那么设两个状态, f1i 是走楼梯到达第 i 楼用的最少时间, f2i 是坐电梯到达第 i 楼用的最少时间, 很容易得到状态转移方程:
f1i = min(f1i-1, f2i-1) + a[i-1] , 走楼梯到达第 i 层的最少时间是到达第 i-1 层的最少时间 + a[i -1].
f2i = min(f2i-1, f1i-1 + c) + b[i-1] , 做电梯到达第 i 层的最少时间是坐电梯到达第 i - 1层的最小时间和走楼梯到 i-1 层的最小时间 + 等电梯的时间这两者中的较小值 + b[i - 1]
注意初态 f11 = 0, f21 = inf, 因为一楼不可能是坐电梯到达的嘛.

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 200010
using namespace std;
const int inf=0x3f3f3f3f;
int n,c,a[MAXN],b[MAXN];
int f1[MAXN],f2[MAXN];
int main(){
scanf("%d%d",&n,&c);
for(int i=1;i<n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++) scanf("%d",&b[i]);
f1[1]=0;f2[1]=inf;
for(int i=2;i<=n;i++){
f1[i]=min(f2[i-1],f1[i-1])+a[i-1];
f2[i]=min(f2[i-1],f1[i-1]+c)+b[i-1];
}
for(int i=1;i<=n;i++) printf("%d ",min(f1[i],f2[i]));
return 0;
}
F. Maximum Weight Subset
介绍一个O(N3)的树形Dp方法.
设状态 f[i,j] 是点 i 到以它为根节点的子树中最近一个选中点的距离大于等于 j 时这颗子树产生的最大贡献.
那么对于 f[i,0] 就是选中点 i 时的最大值,因为每个选中点之间距离要大于 k, 那么转移方程为
$$f\left[ i,0\right] =a\left[ i\right] +\sum ^{son}_{t}f\left[ t,k\right]$$
对于 f[i,w] (0<w<k), 枚举 i 的一个儿子离 i 最近, 它的最近选中点距离当然为 w-1, 为了满足选中点距离大于 k, 并且其他儿子中最近选中点距离 i 不能小于 w, 那么可以得出转移方程:
$$\max _{s\in son}\left\{ f\left[ s,w-1\right] +\sum ^{son}_{t\neq s}\left[ t,\max \left( k-w,w-1\right) \right] \right\}$$
那么注意f[i,j] = max(f[i,j] , f[i,j+1])

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
using namespace std;
int n,k,a[210],f[210][210];
int head[210],to[410],nxt[410],tot=1;
void add(int u,int v){to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
void dfs(int u,int rt){
f[u][0]=a[u];
for(int i=head[u];i;i=nxt[i]){
if(to[i]==rt) continue;
dfs(to[i],u);
}
for(int i=head[u];i;i=nxt[i]){
if(to[i]==rt) continue;
f[u][0]+=f[to[i]][k];
}
for(int w=1;w<=k;w++){
for(int i=head[u];i;i=nxt[i]){
if(to[i]==rt) continue;
int temp=f[to[i]][w-1];
for(int j=head[u];j;j=nxt[j]){
if(to[j]==rt||to[j]==to[i]) continue;
temp+=f[to[j]][max(k-w,w-1)];
}
f[u][w]=max(f[u][w],temp);
}
}
for(int w=k;w>=0;w--) f[u][w]=max(f[u][w+1],f[u][w]);
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,u,v;i<n;i++){scanf("%d%d",&u,&v);add(u,v);add(v,u);}
dfs(1,0);
cout<<f[1][0]<<endl;
return 0;
}
来源:https://www.cnblogs.com/BakaCirno/p/11725407.html
