Codeforces Global Round 16部分题解

A.Median Maximization

比较简单显而易见的贪心,显然前\(\lfloor\frac{m-1}{2} \rfloor\)数是\(0\),后面的数尽可能平均分。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<time.h>
#include<set>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned long long
#define lowbit(i) i&(-i)
#define pb(x) push_back(x)
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=1e5+10,mod=1e9+7;
int T,n,s;
int main(){
    read(T);
    while(T--){
        read(n),read(s);
	printf("%d\n",s/(n-(n+1)/2+1));
    }
}

B.MIN-MEX Cut

首先发现对于\(1\)来说,\(\rm MEX(1)=0,MEX(1...1)=0\)

而对于\(0\)来说,\(\rm MEX(0)=1,MEX(0...0)=1\),因此一段\(0\)的答案最优显然是\(1\)

可以发现的一点是\(\rm MEX(01)=2\),所以答案显然小于\(2\)

因此,我们只需要计算出\(0\)的段数就可以得出答案了

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<time.h>
#include<set>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned long long
#define lowbit(i) i&(-i)
#define pb(x) push_back(x)
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=1e5+10,mod=1e9+7;
int T,n,m;char a[maxn];
int main(){
    read(T);
    while(T--){
	scanf("%s",a+1);n=strlen(a+1);m=0;
	for(rg int i=1;i<=n;i++)if(a[i]!=a[i-1]&&a[i]=='0')m++;
	if(m>=2)m=2;printf("%d\n",m);
    }
}

C.MAX-MEX Cut

这个题目可以说是上面那题的升级版

考虑方法是一致的

对于每一列,有三种情况:

1、同为\(1\),那么\(\rm MEX\begin{bmatrix}1\\1 \end{bmatrix}=0\)

2、同为\(0\),那么\(\rm MEX\begin{bmatrix}0\\0 \end{bmatrix}=1\)

3、不相同,那么\(\rm MEX\begin{bmatrix}0\\1 \end{bmatrix}=2,\rm MEX\begin{bmatrix}1\\0 \end{bmatrix}=2\)

简单分析可知,前两种情况组合可以得到更大的价值。

所以我们只需要前两种情况能组合就组合就行了。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<time.h>
#include<set>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned long long
#define lowbit(i) i&(-i)
#define pb(x) push_back(x)
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=1e5+10,mod=1e9+7;
int T,n,m,b[maxn],vis[maxn];char a[2][maxn];
int main(){
    read(T);
    while(T--){
	read(n);
	scanf("%s",a[0]+1);m=0;scanf("%s",a[1]+1);
	for(rg int i=1;i<=n;i++){
	    if(a[0][i]=='1'&&a[1][i]=='1')b[i]=1;
	    if(a[0][i]=='0'&&a[1][i]=='0')b[i]=2;
	    if(a[0][i]=='0'&&a[1][i]=='1')b[i]=0;
	    if(a[0][i]=='1'&&a[1][i]=='0')b[i]=0;
	}
	for(rg int i=1;i<=n;i++){
	    if(b[i]==0)m+=2;
	    if(b[i]==2)m+=1;
	    if(b[i]==1){
		if(b[i-1]==2&&!vis[i-1])m++;
		else if(b[i+1]==2)vis[i+1]=1,m++;
	    }
	}
	for(rg int i=1;i<=n;i++)vis[i]=b[i]=0;
	printf("%d\n",m);
    }
}

D.Seating Arrangements (hard version)

直接考虑\(\rm hard\ version\)

本题实际上就是模拟。统计出每个数字可以填的范围。

然后贪心的考虑显然要使得当前填的数尽可能小的影响到其他数,也就是每一行优先填最右边的位置

用指针记录一下每个数填过哪些位置就行了

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<time.h>
#include<set>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned long long
#define lowbit(i) i&(-i)
#define pb(x) push_back(x)
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=310,mod=1e9+7;
int T,n,m,ans,a[maxn*maxn],f[maxn][maxn],b[maxn*maxn];
struct oo{int x,y;}l[maxn*maxn],r[maxn*maxn],d[maxn*maxn];
map<int,int>mp;
int main(){
    read(T);
    while(T--){
	memset(f,0,sizeof f);
	memset(l,0,sizeof l);
	memset(r,0,sizeof r);
	memset(d,0,sizeof d);
	memset(b,0,sizeof b);
	memset(a,0,sizeof a);
	mp.clear();
	read(n),read(m);ans=0;
	for(rg int i=1;i<=n*m;i++)read(a[i]),b[i]=a[i];
	sort(b+1,b+n*m+1);int g=0,s=1;
	for(rg int i=1;i<=n*m;i++)if(!mp[b[i]])mp[b[i]]=++g;
	for(rg int i=1;i<=n*m;i++)
	    if(b[i]!=b[i+1]){
		l[mp[b[i]]]=(oo){(s-1)/m+1,(s-1)%m+1};
		r[mp[b[i]]]=(oo){(i-1)/m+1,(i-1)%m+1};
		if(r[mp[b[i]]].x>l[mp[b[i]]].x)d[mp[b[i]]]=(oo){l[mp[b[i]]].x,m};
		else d[mp[b[i]]]=r[mp[b[i]]];
		s=i+1;
	    }
	for(rg int i=1;i<=n*m;i++){
	    int xx=d[mp[a[i]]].x,yy=d[mp[a[i]]].y;
	    for(rg int j=1;j<=yy;j++)if(f[xx][j])ans++;
	    f[xx][yy]=1;d[mp[a[i]]].y--;
	    if(d[mp[a[i]]].x==l[mp[a[i]]].x&&d[mp[a[i]]].y<l[mp[a[i]]].y){
		d[mp[a[i]]].x++,d[mp[a[i]]].y=m;
		if(d[mp[a[i]]].x==r[mp[a[i]]].x)d[mp[a[i]]].y=r[mp[a[i]]].y;
	    }
	    else if(d[mp[a[i]]].y<1){
		d[mp[a[i]]].x++,d[mp[a[i]]].y=m;
		if(d[mp[a[i]]].x==r[mp[a[i]]].x)d[mp[a[i]]].y=r[mp[a[i]]].y;
	    }
	}
	printf("%d\n",ans);
    }
}

E.Buds Re-hanging

考虑每切下一个\(\rm bud\),我们有两种情况:

1、多出一个叶子节点,然后我们将该\(\rm bud\)接到一个叶子上,又可以减少一个叶子节点,总的来说叶子节点数不变

2、叶子节点数不变,然后我们将该\(\rm bud\)接到一个叶子上,减少一个叶子节点,总的来说叶子节点数减少\(1\)

因此切下\(\rm bud\)显然是不劣的

最优的情况显然是\(\rm bud-叶子-bud-叶子\)这样的,所以我们只需要统计出有多少个\(\rm bud\)就行了,然后这样排列就行了

一个细节是处理一下根是否有叶子节点

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<time.h>
#include<set>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned long long
#define lowbit(i) i&(-i)
#define pb(x) push_back(x)
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=2e5+10,mod=1e9+7;
int T,n,m,ans;bool used[maxn];
int cnt,pre[maxn*2],nxt[maxn*2],h[maxn];
void add(int x,int y){
    pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;
    pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt;
}
void dfs(int x,int fa){
    int f=0;
    for(rg int i=h[x];i;i=nxt[i])
	if(pre[i]!=fa){
	    dfs(pre[i],x);
	    if(!used[pre[i]])f=1;
	}
    if(f&&x!=1)used[x]=1,m++;
    if(x==1){
	if(f)ans=-1;
	else ans=0;
    }
}
int main(){
    read(T);
    while(T--){
	read(n);m=cnt=0;
	for(rg int i=1,x,y;i<n;i++)read(x),read(y),add(x,y);
	dfs(1,0);printf("%d\n",ans+n-m*2);
	for(rg int i=1;i<=n;i++)used[i]=0,h[i]=0;
    }
}

F.Points Movement

显然初始情况下就已经满足条件的区间是没用的

然后对点和剩下的区间分别排个序。

\(a_i\)是第\(i\)点的初始坐标

显然的是第\(i\)个点的移动范围一定在\(a_{i-1}\)\(a_{i+1}\)之间,因为如果超过前后两个点,用那两个点一定更优

用这个结论,我们就可以确定出一个点能处理的区间有哪些了。

一个显然的结论是:前\(i\)个点要处理掉所有在\(a_i\)之前的区间

那么我们可以设出\(f[i][j]\)表示当前考虑了前\(i\)个点,处理了前\(j\)个区间的最小代价。

\(i\)个点至少要处理的区间是前\(v_i\)

状态转移略微复杂:

1、假如当前考虑前\(i-1\)个点处理到了前\(k\)个区间,第\(i\)个点只处理到\(v_i\),那么我们只需要向左移动就行了

2、假如当前考虑前\(i-1\)个点处理到了前\(k\)个区间,第\(i\)个点处理前\(t(v_i<t\leq v_{i+1})\)个区间,那么我们就有两种走法,先左移再向右移,或是先向右移再左移,这个地方的代价有些不好处理。因为对于每个\(t\),向右的距离是固定的,两种走法分别计算一下最小值就行了

3、假如当前考虑前\(i-1\)个点处理到了前\(v_i\)个区间,那么第\(i\)个点就可以考虑只右移即可

一共三种情况,该题范围\(2\times 10^5\),所以滚动第一维即可

该题每个点只被访问\(1\)次,区间只被访问\(2\)次,总复杂度\(O(n)\)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<time.h>
#include<set>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned long long
#define lowbit(i) i&(-i)
#define pb(x) push_back(x)
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=2e5+10,mod=1e9+7;
int n,a[maxn],T,m,v[maxn],tot,cc[maxn];
ll f[2][maxn],g[maxn],ans=1e18;
struct oo{int l,r;}d[maxn],c[maxn];
bool cmp(oo a,oo b){return a.l<b.l;}
int main(){
    read(T);
    while(T--){
	read(n),read(m);tot=0;ans=1e18;
	for(rg int i=1;i<=n;i++)read(a[i]);
	sort(a+1,a+n+1);
	for(rg int i=1;i<=m;i++)read(d[i].l),read(d[i].r);
	sort(d+1,d+m+1,cmp);
	for(rg int i=1;i<=m;i++){
	    int s=lower_bound(a+1,a+n+1,d[i].l)-a,t=upper_bound(a+1,a+n+1,d[i].r)-a-1;
	    if(s>t)c[++tot]=d[i],cc[tot]=d[i].r;
	}
	for(rg int i=1;i<=n;i++)v[i]=upper_bound(cc+1,cc+tot+1,a[i])-cc-1;
	v[n+1]=tot;v[0]=0;f[0][0]=0;
	for(rg int i=1;i<=tot;i++)f[0][i]=1e18;
	for(rg int i=1;i<=n;i++){
	    f[i&1][0]=0;
	    for(rg int j=1;j<=tot;j++)f[i&1][j]=f[(i&1)^1][j],g[j]=1e18;
	    int now=2e9;
	    for(rg int j=v[i];j>v[i-1];j--){
		now=min(now,c[j].r);
	        f[i&1][v[i]]=min(f[i&1][v[i]],f[(i&1)^1][j-1]+a[i]-now);
	    }
	    now=2e9;
	    for(rg int j=v[i];j>v[i-1];j--){
		now=min(now,c[j].r);
	        g[v[i]]=min(g[v[i]],f[(i&1)^1][j-1]+2ll*(a[i]-now));
	    }
	    now=-2e9;
	    for(rg int j=v[i]+1;j<=v[i+1];j++){
		now=max(now,c[j].l);
		f[i&1][j]=min(f[i&1][j],f[i&1][v[i]]+2ll*(now-a[i]));
		f[i&1][j]=min(f[i&1][j],g[v[i]]+now-a[i]);
		f[i&1][j]=min(f[i&1][j],f[(i&1)^1][v[i]]+now-a[i]);
	    }
	    ans=min(ans,f[i&1][tot]);
	}
	printf("%lld\n",ans);
    }
}
posted @ 2021-09-16 23:27  蒟蒻--lichenxi  阅读(97)  评论(0编辑  收藏  举报