天天做鬼子和毛子网站的题,有没有点民族自豪感?
补题,但是被反对了,所以不补了,能给我推荐吗😭
T1
题意
给出 \(n\) 个点的无向完全图,你要给所有边定向(每条边正向或者反向有不同的代价),使得该图是强联通的。
\(n \leq 18\)。
题解
Rainybunny,看看你的耳朵。
耳分解说的是,一个强联通分量一定可以通过以下方式构成:
- 任选一个强连通分量中的点 \(x\),初始令集合 \(S=\{x\}\)。
- 每次选择两个(可重的)点 \(u,v\in S\),与一条路径 \(u\to p_1\to p_2\to \ldots\to v\),其中 \(p_i\not\in S\)。
- 将这条路径并入强连通分量,将 \(\{p_i\}\) 并入 \(S\)。
那么就可以用 \(f_S\) 表示,当前集合为 \(S\) 时的最小代价。
转移要添加一条路径,这个代价很大的,于是乎我们分步转移:\(dp_{S,u,v,now}\) 表示我这条链的起终点是 \(u\to v\),现在已经走到了 \(now\),并且当前集合与 \(u\to now\) 这条路径的并集为 \(S\)。
然后我们发现这个 \(u\) 完全没必要的,删去这一维就 \(O(2^n n^3)\) 啦。一个细节是,路径不能是 \(u\to v\to u\) 这种东西,那么从 \(dp\) 更新 \(f\) 时多枚举一条边即可,复杂度不变。
有人可能要问,这个代码块为什么黑不拉几的,因为另外一个 tab 长度是 8 啊!所以我选了一个比较好看的,虽然黑是黑了点。
#include<bits/stdc++.h>
#define cl(x,t) memset(x,t,sizeof x)
#define FOR(i,a,b) for(int i=a,i##i=b;i<=i##i;++i)
#define ROF(i,a,b) for(int i=a,i##i=b;i>=i##i;--i)
typedef long long ll;
using namespace std;
const int N=20,S=(1<<18)+7,inf=1e9+7;
int n,a[N][N],L;
ll dp[S][N][N],f[S],ans;
inline void chk(ll &x,ll y){if(x>y)x=y;}
signed main(){
scanf("%d",&n),L=(1<<n)-1;
FOR(i,0,n-1)FOR(j,0,n-1)scanf("%d",a[i]+j);
FOR(i,0,n-1)FOR(j,0,i)if(~a[i][j]){
ll x=min(a[i][j],a[j][i]);
ans+=x,a[i][j]-=x,a[j][i]-=x;
}
else a[i][j]=a[j][i]=inf;
cl(dp,63),cl(f,63),f[1]=0;
FOR(s,1,L)if(s&1){
FOR(i,0,n-1)if(s>>i&1)FOR(j,0,n-1)if(s>>j&1){
ll d=min(f[s],dp[s][i][j]);
FOR(k,0,n-1)if(!(s>>k&1)){
int t=s|(1<<k);
chk(dp[t][k][j],d+a[i][k]);
if(i!=j)chk(f[t],d+a[i][k]+a[k][j]);
}
}
}
ans+=f[L],printf("%lld",ans>inf?-1:ans);
return 0;
}
0808 模拟赛 T2
题意
给定长度为 \(n\) 的序列 \(a_1,a_2,\ldots,a_n\),还有一个正整数 \(k\)。
单点修改,每次修改后找一个长度为 \(k\) 的区间,最大化最大值与非严格次大值的和。
\(n \leq 10^6\), \(a_i \leq 10^9\)。
题解
为什么大家都会,就我不会/kk
很明显,这是让你最大化两个距离 \(\leq k\) 的数之和。如果我们只把数往大了改,那显然每次更改查询一次最值就好。
但有可能会把数往小了改,那就把这次操作看成删除了这个数,线段树分治就完事了,\(O(q \log q\log n)\)。
#include<bits/stdc++.h>
#define pbk emplace_back
#define lc k<<1
#define rc lc|1
#define mid ((l+r)>>1)
#define seg int k=1,int l=1,int r=n
#define LS lc,l,mid
#define RS rc,mid+1,r
#define FOR(i,a,b) for(int i=a,i##ED=b;i<=i##ED;++i)
#define ROF(i,a,b) for(int i=a,i##ED=b;i>=i##ED;--i)
typedef long long ll;
using namespace std;
const int N=1e6+7;
int n,K,m,a[N],b[N],x[N],y[N],mx[N<<2],id[N],ans[N<<2],cnt[N],rs;
inline void pup(int k){mx[k]=max(mx[lc],mx[rc]);}
void build(seg){
if(l==r)mx[id[l]=k]=a[l];
else build(LS),build(RS),pup(k);
}
int qry(int x,int y,seg){
if(l>y||r<x)return 0;
if(l>=x&&r<=y)return mx[k];
return max(qry(x,y,LS),qry(x,y,RS));
}
inline int get(int x){return max(qry(x-K,x-1),qry(x+1,x+K));}
int upd(int x,int v){
int k=id[x];
for(mx[k]=v;k>>=1;pup(k));
return v+get(x);
}
void solve(seg){
if(l==r){
printf("%d\n",max(ans[k],get(x[l])+y[l]));
return;
}
ans[lc]=ans[rc]=ans[k];
ROF(i,r,mid+1){
int u=x[i];
if(!--cnt[u])ans[lc]=max(ans[lc],upd(u,b[u]));
}
solve(LS);
FOR(i,mid+1,r)if(!cnt[x[i]]++)upd(x[i],0);
FOR(i,l,mid){
int u=x[i];
if(!--cnt[u])ans[rc]=max(ans[rc],upd(u,y[i]));
b[u]=y[i];
}
solve(RS);
ROF(i,mid,l)if(!cnt[x[i]]++)upd(x[i],0);
}
int main(){
scanf("%d%d%d",&n,&K,&m),--K;
FOR(i,1,n)scanf("%d",a+i),b[i]=a[i];
build();
FOR(i,1,n)rs=max(rs,a[i]+get(i));
printf("%d\n",rs),memset(mx,0,sizeof mx);
FOR(i,1,m)scanf("%d%d",x+i,y+i),++cnt[x[i]];
FOR(i,1,n)if(!cnt[i])ans[1]=max(ans[1],upd(i,a[i]));
if(m)solve(1,1,m);
return 0;
}