天天做鬼子和毛子网站的题,有没有点民族自豪感?

补题,但是被反对了,所以不补了,能给我推荐吗😭


T1

题意

给出 \(n\) 个点的无向完全图,你要给所有边定向(每条边正向或者反向有不同的代价),使得该图是强联通的。

\(n \leq 18\)

题解

Rainybunny,看看你的耳朵。

耳分解说的是,一个强联通分量一定可以通过以下方式构成:

  1. 任选一个强连通分量中的点 \(x\),初始令集合 \(S=\{x\}\)
  2. 每次选择两个(可重的)点 \(u,v\in S\),与一条路径 \(u\to p_1\to p_2\to \ldots\to v\),其中 \(p_i\not\in S\)
  3. 将这条路径并入强连通分量,将 \(\{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;
}
posted @ 2022-08-09 16:34  Ilith  阅读(303)  评论(8编辑  收藏  举报