C. Paint
题目描述
给你一个长度为 (n) 的颜色数组,每次可以选择一个位置修改它的颜色,此时与他相邻的极长连续相同颜色段也会改变颜色,问把所有位置变同色的最小操作次数。
(nleq 3cdot 10^3)
解法
因为每次操作的是一个极长同色连续段,所以可以考虑用区间 (dp)
考虑暴力操作需要用 长度(-1) 步,但是形如 (aba) 操作中间可以少用一步,所以设 (dp[l][r]) 表示把区间 ([l,r]) 染成同色的最大减少操作次数,最后的答案是 (n-1-dp[1][n])
转移我们需要以减少操作为导向,所以考虑枚举 (a...a) 这种情况,我们找到所有 (s[i]=s[l](l<ileq r)):
为什么是 (dp[i][r]) 呢?注意我们的状态定义中并不涉及最终颜色,但是不难观察到:可以通过最小操作步数使一个区间变成初始时它边界上的颜色,所以这两段就可以合并了。
还有一种简单的情况是直接继承,不操作:
暴力转移时间复杂度 (O(20n^2))
总结
(dp) 状态定义注意 (min,max) 的转化,这道如果不换成 ( t max) 就不好用到相同颜色数量有限的条件,因为 (max) 是以减少步数为导向的,这是一种正难则反的思想。
//Take me to the top , I am ready for...
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 3005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,a[M],dp[M][M];vector<int> b[M];
void upd(int &x,int y) {x=max(x,y);}
void work()
{
n=read();
for(int i=1;i<=n;i++)
{
b[i].clear();
for(int j=1;j<=n;j++) dp[i][j]=0;
}
for(int i=1;i<=n;i++)
{
a[i]=read();
b[a[i]].push_back(i);
}
for(int l=n;l>=1;l--)
for(int r=l;r<=n;r++)
{
dp[l][r]=dp[l+1][r];
for(auto x:b[a[l]]) if(l<x && x<=r)
upd(dp[l][r],dp[l+1][x-1]+1+dp[x][r]);
}
printf("%d
",n-1-dp[1][n]);
}
signed main()
{
T=read();
while(T--) work();
}
D. Bridge Club
题目描述
有 (2^n) 个人,编号为 (0 ightarrow 2^n-1),如果两个人二进制位最多相差 (1) 就可以配对,每个人最多配对一次,每对的得分为两个人的点权之和,问最多配 (k) 对的最大得分。
(nleq 20,kleq 200)
解法
可以发现至多 ((2n-1)(k-1)+1) 条边有用,桶排之后暴力网络流即可。
总结
缩小问题规模(只考虑和答案有关的量),寻找等价类,是解决不仅限于匹配问题的重要方法。
//I was the king under your control~~
#include <cstdio>
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
const int M = 20005;
const int N = 1100005;
const int inf = 0x3f3f3f3f;
#define pii pair<int,int>
#define mp make_pair
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,t,cnt,a[N],b[N],c[N];vector<pii> w[N<<1];
int S,T,tot,ans,f[M],in[M],dis[M],flow[M],pre[M],lst[M];
struct edge
{
int v,c,f,next;
}e[N];
void add(int u,int v,int F,int c)
{
e[++tot]=edge{v,c,F,f[u]},f[u]=tot;
e[++tot]=edge{u,-c,0,f[v]},f[v]=tot;
}
int bfs()
{
for(int i=0;i<=T;i++) dis[i]=-inf;
queue<int> q;q.push(S);in[S]=1;
dis[S]=flow[T]=0;flow[S]=inf;
while(!q.empty())
{
int u=q.front();q.pop();in[u]=0;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v,c=e[i].c;
if(e[i].f && dis[v]<dis[u]+c)
{
dis[v]=dis[u]+c;
pre[v]=u;lst[v]=i;
flow[v]=min(flow[u],e[i].f);
if(!in[v]) q.push(v),in[v]=1;
}
}
}
return flow[T]>0;
}
signed main()
{
n=read();k=read();
m=1<<n;t=(2*n-1)*(k-1)+1;tot=1;
for(int i=0;i<m;i++)
{
a[i]=read();
b[i]=__builtin_popcount(i);
}
for(int i=0;i<(1<<n);i++) if(b[i]&1)
for(int j=0;j<n;j++)
{
int to=i^(1<<j);
w[a[i]+a[to]].push_back(mp(i,to));
}
for(int i=2000000;i>=0;i--)
{
for(auto x:w[i])
{
int u=x.first,v=x.second;
if(!c[u]) c[u]=++cnt;
if(!c[v]) c[v]=++cnt;
add(c[u],c[v],1,i);
t--;if(t<0) break;
}
if(t<0) break;
}
S=0;T=++cnt;
for(int i=0;i<m;i++) if(c[i])
{
if(b[i]&1) add(S,c[i],1,0);
else add(c[i],T,1,0);
}
while(bfs())
{
if(dis[T]<=0) break;
int zy=T,tmp=min(flow[T],k);
ans+=dis[T]*tmp;k-=tmp;
if(k==0) break;
while(zy!=S)
{
e[lst[zy]].f-=tmp;
e[lst[zy]^1].f+=tmp;
zy=pre[zy];
}
}
printf("%d
",ans);
}
F. Stations
题目描述
Caught up in confusion . Need a resolution.
有 (n) 个塔台排成一排,设第 (i) 个塔台的高度是 (h_i),覆盖范围是 (w_i),(i) 能覆盖 (j) 的充要条件是:
- (ileq jleq w_i),(forall i<kleq j,h_k<h_i)
一开始所有塔台的高度都为 (0),覆盖范围都为 (i),有下列两种操作:
- (op=1),重建操作,把塔台 (x) 的高度重建成当前最高,覆盖范围设置成 (y)
- (op=2),询问操作,设 (b_i) 表示覆盖它的塔台数量,求 (sum_{lleq ileq r} b_i)
(n,qleq 2cdot 10^5)
解法
这道题,是我自己做出来的[骄傲.jpg]
由于每次重建都会获得最高的塔台,而且它的有效覆盖范围是 (y),我们只需要考虑这个塔台对其他塔台有效覆盖范围的影响即可,不难发现是对 ([1,x)) 的塔台对 (x-1) 取 (min)
既然是取 (min) 操作我们可以考虑势能线段树,所以每个塔台的有效覆盖范围是不难维护的,但是这样难以处理询问,我们不妨再拿一棵线段树维护每个点的被覆盖次数,再更新有效覆盖范围的时候更新它即可。
具体算法:势能线段树在 (mx>x-1>cx) 的时候整体取消一个区间的覆盖即可,时间复杂度 (O(nlog^2 n))
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 800005;
#define ll long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,mx[M],cx[M],num[M];ll sum[M],tag[M];
//segment tree II : support simple addition
void Down(int i,int l,int r)
{
if(!tag[i]) return ;
int mid=(l+r)>>1,c=tag[i];
sum[i<<1]+=c*(mid-l+1);tag[i<<1]+=c;
sum[i<<1|1]+=c*(r-mid);tag[i<<1|1]+=c;
tag[i]=0;
}
void Add(int i,int l,int r,int L,int R,int c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
sum[i]+=(r-l+1)*c;tag[i]+=c;
return ;
}
int mid=(l+r)>>1;Down(i,l,r);
Add(i<<1,l,mid,L,R,c);
Add(i<<1|1,mid+1,r,L,R,c);
sum[i]=sum[i<<1]+sum[i<<1|1];
}
ll Ask(int i,int l,int r,int L,int R)
{
if(l>R || L>r) return 0;
if(L<=l && r<=R) return sum[i];
int mid=(l+r)>>1;Down(i,l,r);
return Ask(i<<1,l,mid,L,R)+
Ask(i<<1|1,mid+1,r,L,R);
}
//segment tree I : support interval Min
void down(int i)
{
mx[i<<1]=min(mx[i<<1],mx[i]);
mx[i<<1|1]=min(mx[i<<1|1],mx[i]);
}
void up(int i)
{
num[i]=0;
mx[i]=max(mx[i<<1],mx[i<<1|1]);
cx[i]=max(cx[i<<1],cx[i<<1|1]);
if(mx[i]==mx[i<<1]) num[i]+=num[i<<1];
if(mx[i]==mx[i<<1|1]) num[i]+=num[i<<1|1];
if(mx[i]!=mx[i<<1]) cx[i]=max(cx[i],mx[i<<1]);
if(mx[i]!=mx[i<<1|1]) cx[i]=max(cx[i],mx[i<<1|1]);
}
void ins(int i,int l,int r,int id,int c)
{
if(l==r)
{
Add(1,1,n,id,mx[i],-1);
mx[i]=c;num[i]=1;
Add(1,1,n,id,mx[i],1);
return ;
}
int mid=(l+r)>>1;down(i);
if(mid>=id) ins(i<<1,l,mid,id,c);
else ins(i<<1|1,mid+1,r,id,c);
up(i);
}
void zxy(int i,int l,int r,int c)
{
if(mx[i]<=c) return ;
if(mx[i]>c && c>cx[i])
{
Add(1,1,n,c+1,mx[i],-num[i]);
mx[i]=c;return ;
}
if(l==r)
{
if(c<mx[i]) Add(1,1,n,c+1,mx[i],-1);
mx[i]=c;return ;
}
int mid=(l+r)>>1;down(i);
zxy(i<<1,l,mid,c);
zxy(i<<1|1,mid+1,r,c);
up(i);
}
void upd(int i,int l,int r,int L,int R,int c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
zxy(i,l,r,c);
return ;
}
int mid=(l+r)>>1;down(i);
upd(i<<1,l,mid,L,R,c);
upd(i<<1|1,mid+1,r,L,R,c);
up(i);
}
signed main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
ins(1,1,n,i,i);
for(int i=1;i<=m;i++)
{
int op=read(),x=read(),y=read();
if(op==1)
{
ins(1,1,n,x,y);
upd(1,1,n,1,x-1,x-1);
}
else
printf("%lld
",Ask(1,1,n,x,y));
}
}