比较简单显而易见的贪心,显然前\(\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)); } }
首先发现对于\(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); } }
这个题目可以说是上面那题的升级版
考虑方法是一致的
对于每一列,有三种情况:
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); } }
直接考虑\(\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); } }
考虑每切下一个\(\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; } }
显然初始情况下就已经满足条件的区间是没用的
然后对点和剩下的区间分别排个序。
\(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); } }