A.Median Maximization
比较简单显而易见的贪心,显然前(lfloorfrac{m-1}{2} floor)数是(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
",s/(n-(n+1)/2+1));
}
}
B.MIN-MEX Cut
首先发现对于(1)来说,( m MEX(1)=0,MEX(1...1)=0)
而对于(0)来说,( m MEX(0)=1,MEX(0...0)=1),因此一段(0)的答案最优显然是(1)
可以发现的一点是( m 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
",m);
}
}
C.MAX-MEX Cut
这个题目可以说是上面那题的升级版
考虑方法是一致的
对于每一列,有三种情况:
1、同为(1),那么( m MEXegin{bmatrix}1\1 end{bmatrix}=0)
2、同为(0),那么( m MEXegin{bmatrix}0\0 end{bmatrix}=1)
3、不相同,那么( m MEXegin{bmatrix}0\1 end{bmatrix}=2, m MEXegin{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
",m);
}
}
D.Seating Arrangements (hard version)
直接考虑( m 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
",ans);
}
}
E.Buds Re-hanging
考虑每切下一个( m bud),我们有两种情况:
1、多出一个叶子节点,然后我们将该( m bud)接到一个叶子上,又可以减少一个叶子节点,总的来说叶子节点数不变
2、叶子节点数不变,然后我们将该( m bud)接到一个叶子上,减少一个叶子节点,总的来说叶子节点数减少(1)
因此切下( m bud)显然是不劣的
最优的情况显然是( m bud-叶子-bud-叶子)这样的,所以我们只需要统计出有多少个( m 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
",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<tleq v_{i+1}))个区间,那么我们就有两种走法,先左移再向右移,或是先向右移再左移,这个地方的代价有些不好处理。因为对于每个(t),向右的距离是固定的,两种走法分别计算一下最小值就行了
3、假如当前考虑前(i-1)个点处理到了前(v_i)个区间,那么第(i)个点就可以考虑只右移即可
一共三种情况,该题范围(2 imes 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
",ans);
}
}