20220509

  • tot 160 (300)
  • rk 3/12

魔法宝石

100pts

\(\log^{2}\) 轻松过,白纠结了

考场代码
const int N = 5e5+5, X = 1e9;
int n,a[N],f[19][N];
Vi add[N],del[N];
gp_hash_table<int,int> cnt;

int _max(int i,int j) { return a[i]>a[j] ? i : j; }
int maxx(int l,int r) {
	int k = __lg(r-l+1);
	return _max(f[k][l],f[k][r-(1<<k)+1]);
}

void slv(int l,int r) {
	if( r-l+1 < 3 ) return;
	int mid = maxx(l,r);
	if( mid-l < r-mid )
		Rep(i,l,mid) add[r].pb(a[mid]-a[i]), del[mid].pb(a[mid]-a[i]);
	else
		For(i,mid+1,r) add[mid-1].pb(a[mid]-a[i]), del[l-1].pb(a[mid]-a[i]);
	slv(l,mid-1), slv(mid+1,r);
}

signed main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout);
	io>>n; For(i,1,n) io>>a[i];
	iota(f[0]+1,f[0]+n+1,1);
	For(i,1,18) For(j,1,n-(1<<i)+1) f[i][j] = _max(f[i-1][j],f[i-1][j+(1<<i-1)]);
	slv(1,n);
	LL ans = 0;
	For(i,1,n) {
		++cnt[a[i]];
		for(int j : add[i]) if( cnt.find(j) != cnt.end() ) ans += cnt[j];
		for(int j : del[i]) if( cnt.find(j) != cnt.end() ) ans -= cnt[j];
	}
	io<<ans;
	return 0;
}

商人

20pts, max = 100pts

看数据范围基本可以放弃 DP,考虑组合数

先算答案 \(\ge i\) 的方案数,做差得到 \(=i\)。枚举左括号个数 \(x\in[i,n-i]\),不妨设 \(x\ge n-x\)(否则把下面的过程全部逆序),把 ( 看作 \(1\)) 看作 \(-1\),记前缀和最小值为 \(y\le 0\),匹配的括号数即为 \(n-x+y\)。相当于要统计 \(n-x+y\ge i\iff y\ge i+x-n\) 的方案数

( 看作向右上走,) 看作向右下走,等于从 \((0,0)\) 走到 \((n,2x-n)\) 且不触碰 \(y=i+x-n-1\) 的方案数,为 \({n\choose x}-{n\choose n-i+1}\)(注意不是 \(n\choose i-1\))。

最后需要对每个 \(i\) 计算 \(\sum_{x=i}^{n-i}{n\choose x}-{n\choose n-i+1}\),时间复杂度 \(O(n)\)

code
const int N = 1e6+5;
int ty,n,m;
LL ans[N],fac[N],ivf[N];

auto C=[](int n,int m) { return fac[n] * ivf[m] %mod * ivf[n-m] %mod; };

signed main() { freopen("b.in","r",stdin); freopen("b.out","w",stdout);
	io>>ty>>n; if( ty == 1 ) io>>m;
	fac[0] = fac[1] = ivf[0] = ivf[1] = 1;
	For(i,2,n) fac[i] = fac[i-1]*i%mod, ivf[i] = (mod-mod/i)*ivf[mod%i]%mod;
	For(i,2,n) (ivf[i] *= ivf[i-1]) %=mod;
	LL s = Pow(2,n);
	For(i,0,n/2)
		ans[i] = Mod(s - (n-2*i+1) * C(n,n+1-i) %mod),
		ckadd(s,-Mod(C(n,i)+C(n,n-i)));
	Rep(i,0,n/2) ckadd(ans[i],-ans[i+1]);
	if( ty == 1 ) io<<ans[m]%mod;
	else {
		LL res = 0, bs = 1;
		For(i,0,n/2) (res += ans[i] * bs) %=mod, (bs *= 233) %=mod;
		io<<res;
	}
	return 0;
}

防御工事

40pts, max = 100pts

想到了结论,但暴力试了下发现不对(事实上是写挂了),所以没管,还是应该严谨证明一下。后来给树写了个『幻想乡战略游戏』,写一半发现复杂度要乘上度数,这题没保证。。。好在还是数据水过了

考虑树的部分分。结论是建虚树后首都一定在虚树点上。证明:首都显然不会在虚树外,若在虚树边上,调整到权值和较大的端点一定不劣。在虚树上换根 DP 求出每个点为首都的答案即可

图的话先建圆方树,结论类似。区别在于首都不能建在方点上,把边上与方点相邻的圆点也加入虚树即可。写起来有点烦

code
const int N = 4e5+5;
int n,m,q,val[N];
Vi a,b;

namespace T {
int ind,fa[N],dep[N],dis[N],siz[N],son[N],top[N],dfn[N];
Vi e[N];
#define subtree(u,v) (dfn[u]<=dfn[v]&&dfn[v]<dfn[u]+siz[u])
void adde(int x,int y) { e[x].pb(y), e[y].pb(x); }
void dfs1(int u) {
	dep[u] = dep[fa[u]]+1, dis[u] = dis[fa[u]]+(u<=n), siz[u] = 1;
	for(int v : e[u]) if( v != fa[u] ) {
		fa[v] = u, dfs1(v), siz[u] += siz[v];
		if( siz[v] > siz[son[u]] ) son[u] = v;
	}
}
void dfs2(int u,int t) {
	top[u] = t, dfn[u] = ++ind;
	if( son[u] ) dfs2(son[u],t);
	for(int v : e[u]) if( v != fa[u] && v != son[u] ) dfs2(v,v);
}
int lca(int u,int v) {
	for(; top[u] != top[v]; u = fa[top[u]])
		if( dep[top[u]] < dep[top[v]] ) swap(u,v);
	return dep[u]<dep[v] ? u : v;
}
int jmp(int u,int v) {
	if( !subtree(u,v) ) return fa[u];
	int x; while( top[u] != top[v] ) v = fa[x=top[v]];
	return u==v ? x : son[u];
}
void main() { dfs1(1), dfs2(1,1); }
} using namespace T;

namespace VT {
LL ans,f[N];
Vi e[N];
void dfs(int u,int fa) {
	for(int v : e[u]) {
		dfs(v,u);
		if( u > n ) b.pb(jmp(u,v)); if( v > n ) b.pb(jmp(v,u));
	}
}
void bld() {
static int t,s[N];
auto cmp=[](int x,int y) { return dfn[x] < dfn[y]; };
	sort(all(a),cmp), s[t=1] = 1, b = {1};
	for(int v : a) if( s[t] != v ) {
		int u = lca(s[t],v);
		while( t > 1 && dep[s[t-1]] >= dep[u] ) e[s[t-1]].pb(s[t]), --t;
		if( s[t] != u ) e[u].pb(s[t]), s[t] = u, b.pb(u);
		s[++t] = v, b.pb(v);
	} while( --t ) e[s[t]].pb(s[t+1]);
	dfs(1,0), sort(all(b),cmp);
	for(int i : b) e[i].clear();
	s[t=1] = b[0];
	for(int u : b) if( s[t] != u ) {
		while( !subtree(s[t],u) ) --t;
		e[s[t]].pb(u), s[++t] = u;
	}
}
void clr() {
	for(int i : b) f[i] = val[i] = 0, e[i].clear();
	a.clear();
}
void dfs1(int u) {
	for(int v : e[u])
		dfs1(v), f[u] += f[v]+val[v]*(dis[v]-dis[u]), val[u] += val[v];
}
void dfs2(int u) {
	if( u <= n ) ckmin(ans,f[u]);
	for(int v : e[u])
		f[v] = f[u] - val[v]*(dis[v]-dis[u]) + (m-val[v])*(dis[fa[v]]-dis[fa[u]]),
		dfs2(v);
}
void main() {
	bld();
	ans = LLONG_MAX, dfs1(1), dfs2(1);
	io<<(n-1ll)*m-ans<<endl;
	clr();
}
} using namespace VT;

namespace G {
int nn,ind,dfn[N],low[N];
bool cut[N];
Vi e[N];
void adde(int x,int y) { e[x].pb(y), e[y].pb(x); }
void tarjan(int u) {
static int t,s[N];
	dfn[u] = low[u] = ++ind, s[++t] = u;
	int son = 0;
	for(int v : e[u])
		if( dfn[v] ) ckmin(low[u],dfn[v]);
		else {
			tarjan(v), ckmin(low[u],low[v]);
			if( dfn[u] <= low[v] ) {
				if( u != 1 || son++ ) cut[u] = 1;
				++nn;
				do T::adde(n+nn,s[t]); while( v != s[t--] );
				T::adde(n+nn,u);
			}
		}
}
}

signed main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout);
	io>>n>>m>>q; For(i,1,m, x,y) io>>x>>y, G::adde(x,y);
	G::tarjan(1), T::main();
	while( q-- ) {
		io>>m; For(i,1,m, x) io>>x, ++val[x], a.pb(x);
		VT::main();
	}
	return 0;
}
posted @ 2022-05-09 20:46  401rk8  阅读(58)  评论(0编辑  收藏  举报