20210823 数数,数树,鼠树,ckw的树

考场

乍一看都不好做

仔细想想发现 T1 的绝对值特别好,轮流选剩余的最大/最小值就行了
T2 又要计数,直接想部分分,发现一个 sb 容斥就有 35ps(但数据锅了,只有 25pts)
T3 什么玩意,发现线段树不会操作 6(线段树分裂啊,昨天刚打了板子),LCT 不会操作 2
,但 sub3(只维护黑点) 4(线段树维护序列) 都会
T4 高消直接死了,一分都没有。两个 sub 都是链,什么情况(考场上把 \(1\) 看成 \(i\)了)

迅速写+拍完前两题
想了想 T4 的系数递推,但列出的式子很不可做;貌似也不能高消一次求出所有答案,puts 样例了
感觉 T3 是比较擅长的题型,又想了很久,sub2 也会了(确定联通块后线段树),但直到 10.20 才开始写,最终只写完了 sub1 4

res

rk3 100+28+25+0(显示的是 rk4,因为出榜时 T3 在 judging。。。)

rk1 张王美誉 100+11+60+0
rk2 牛宏昊 100+44+10+0
rk4 付文暄 90+11+10+20(显示的是 rk3 的氪金玩家

总结

想的太浅了,很多地方就差一点,还是要多思考,不要用写代码的勤奋掩盖思维的懒惰。
T3 正解就是在 sub3 的基础上加了个树剖、线段树,T4 给了菊花,sub3 的做法很明显(不过看错题也没啥好说的)

sol

T1

模拟。考场代码:

const int N = 3e5+5;
int n;
LL a[N];

LL ans,pre[N],suf[N];

signed main() {
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	read(n);
	For(i,1,n) read(a[i]); sort(a+1,a+n+1);
	For(i,1,n) pre[i] = pre[i-1] + a[i];
	rFor(i,n,1) suf[i] = suf[i+1] + a[i];
	for(int i = 1, l = 0, r = n+1; i <= n; ++i) {
		if( i & 1 ) {
			++l;
			ans += (a[l] * (l-1) - pre[l-1]) + (suf[r] - a[l] * (n-r+1));
		} else {
			--r;
			ans += (a[r] * l - pre[l]) + (suf[r+1] - a[r] * (n-r));
		}
		write(ans);
	}
	return iocl();
}
T2

发现暴力容斥时边集具体是什么并不重要,只要知道有多少条边即可。考虑树形 DP,设 \(f[u,i,0/1/2/3]\) 为子树 \(u\) 选 \(i\) 条边不合法,点 \(u\) 不选/选入边/选出边/都选的方案数。(显然一个点不能同时使两条入边/出边不合法)

const int N = 5e3+5, mod = 998244353;
int n,mm=1,head[N],to[N*2],w[N*2],nxt[N*2];

int siz[N];
LL ans,fac[N],f[N][N][4],g[N][4];

void dfs(int u,int fa) {
	siz[u] = 1, f[u][0][0] = 1;
	for(int e = head[u], v; v = to[e], e; e = nxt[e]) if( v != fa ) {
		dfs(v,u);
		memset(g,0,sizeof g);
		For(i,0,siz[u]) For(j,0,siz[v]) {
			LL fv = (f[v][j][0]+f[v][j][1]+f[v][j][2]+f[v][j][3]) %mod;
			For(k,0,3) g[i+j][k] += f[u][i][k] * fv %mod;
			if( w[e] )
				g[i+j+1][2] += f[u][i][0] * (f[v][j][0]+f[v][j][2]) %mod,
				g[i+j+1][3] += f[u][i][1] * (f[v][j][0]+f[v][j][2]) %mod;
			else
				g[i+j+1][1] += f[u][i][0] * (f[v][j][0]+f[v][j][1]) %mod,
				g[i+j+1][3] += f[u][i][2] * (f[v][j][0]+f[v][j][1]) %mod;
		}
		siz[u] += siz[v];
		For(i,1,siz[u]) For(j,0,3) f[u][i][j] = g[i][j] %mod;
	}
}

signed main() {
	read(n);
	fac[0] = 1; For(i,1,n) fac[i] = fac[i-1] * i %mod;
	For(i,1,n-1) {
		int x,y; read(x,y);
		to[++mm] = y, w[mm] = 1, nxt[mm] = head[x], head[x] = mm;
		to[++mm] = x, w[mm] = 0, nxt[mm] = head[y], head[y] = mm;
	}
	dfs(1,0);
	For(i,0,n-1)
		ans += (i+1&1?1:-1) * fac[n-i] * (f[1][i][0]+f[1][i][1]+f[1][i][2]+f[1][i][3]) %mod;
	write((ans%mod+mod)%mod);
	return iocl();
}
T3

好题,需要很多 DS 技巧

发现每个白点的权值并不重要,只要知道它归属的黑点即可(可以树剖+set 单 \(\log\) 维护)。线段树维护每个黑点的权值和管辖点数,BIT 辅助
细节较多。

typedef unsigned U;

const int N = 3e5+5;
int n,m,fa[N];

int ind,dep[N],siz[N],son[N],top[N],dfn[N],rdfn[N];
VI to[N];
set<int> s[N];
void dfs1(int u) {
	dep[u] = dep[fa[u]]+1, siz[u] = 1;
	for(int v : to[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, rdfn[ind] = u;
	if( son[u] ) dfs2(son[u],t);
	for(int v : to[u]) if( v != son[u] ) dfs2(v,v);
}
int belong(int u) {
	for(int v; v = top[u]; u = fa[top[u]])
		if( !s[v].empty() && *s[v].begin() <= dep[u] ) {
			auto it = s[v].upper_bound(dep[u]);
			return rdfn[dfn[u]-(dep[u]-*--it)];
		}
}

struct BIT {
	U t1[N],t2[N];
	void add(int i,U x) { for(int j=i;j<=n;j+=j&-j) t1[j] += x, t2[j] += i*x; }
	U sum(int i)
		{ U res=0; for(int j=i;j;j-=j&-j) res += (i+1)*t1[j]-t2[j]; return res; }
	void modify(int l,int r,U x) { add(l,x), add(r+1,-x); }
	U query(int l,int r) { return sum(r) - sum(l-1); }
} bit;

#define ls (u<<1)
#define rs (u<<1|1)
struct Node { int l,r; U siz,val,sum,add; } t[N*4];
void up(int u) {
	t[u].siz = t[ls].siz + t[rs].siz,
	t[u].val = t[ls].val + t[rs].val,
	t[u].sum = t[ls].sum + t[rs].sum;
}
void down(int u,U x) { t[u].val += x, t[u].sum += t[u].siz*x, t[u].add += x; }
void down(int u) { down(ls,t[u].add), down(rs,t[u].add), t[u].add = 0; }
void build(int u,int l,int r) {
	t[u].l = l, t[u].r = r;
	if( l == r ) return;
	int mid = l+r>>1;
	build(ls,l,mid), build(rs,mid+1,r);
}
void addsiz(int u,int p,U x) {
	if( t[u].l == t[u].r ) return t[u].siz += x, t[u].sum += x*t[u].val, void();
	down(u);
	addsiz( p<=t[ls].r?ls:rs ,p,x);
	up(u);
}
void addval(int u,int l,int r,U x) {
	if( l <= t[u].l && t[u].r <= r ) return down(u,x);
	down(u);
	if( l <= t[ls].r ) addval(ls,l,r,x);
	if( t[rs].l <= r ) addval(rs,l,r,x);
	up(u);
}
void covval(int u,int p,U x) {
	if( t[u].l == t[u].r ) return t[u].val = x, t[u].sum = x*t[u].siz, void();
	down(u);
	covval( p<=t[ls].r?ls:rs ,p,x);
	up(u);
}
U qsiz(int u,int l,int r) {
	if( l > r ) return 0;
	if( l <= t[u].l && t[u].r <= r ) return t[u].siz;
	down(u);
	U res = 0;
	if( l <= t[ls].r ) res = qsiz(ls,l,r);
	if( t[rs].l <= r ) res += qsiz(rs,l,r);
	return res;
}
U qval(int u,int p) {
	if( t[u].l == t[u].r ) return t[u].val;
	down(u);
	return qval( p<=t[ls].r?ls:rs ,p);
}
U qsum(int u,int l,int r) {
	if( l > r ) return 0;
	if( l <= t[u].l && t[u].r <= r ) return t[u].sum;
	down(u);
	U res = 0;
	if( l <= t[ls].r ) res = qsum(ls,l,r);
	if( t[rs].l <= r ) res += qsum(rs,l,r);
	return res;
}
#undef ls
#undef rs

signed main() {
	read(n,m);
	For(i,2,n) read(fa[i]), to[fa[i]].pb(i);
	dfs1(1), dfs2(1,1), build(1,1,n);
	s[1].insert(1), addsiz(1,1,n);
	while( m-- ) {
		int op,u,v,l,r; U x,sizu; read(op,u); l = dfn[u], r = dfn[u]+siz[u]-1;
		switch(op) {
			case 1: write(qval(1,dfn[belong(u)])+bit.query(l,l)); break;
			case 2: read(x); addval(1,l,l,x); break;
			case 3:
				write(qval(1,dfn[belong(u)])*(siz[u]-qsiz(1,l+1,r)) +
					  qsum(1,l+1,r) + bit.query(l,r)); 
				break;
			case 4: read(x); addval(1,l,r,x); break;
			case 5:
				v = belong(u), s[top[u]].insert(dep[u]);
				sizu = siz[u]-qsiz(1,l+1,r);
				covval(1,l,qval(1,dfn[v])), addsiz(1,l,sizu);
				addsiz(1,dfn[v],-sizu);
				break;
			default:
				v = belong(fa[u]), s[top[u]].erase(dep[u]);
				sizu = siz[u]-qsiz(1,l+1,r);
				U valu = qval(1,l), valv = qval(1,dfn[v]);
				covval(1,l,0), addsiz(1,l,-sizu);
				addsiz(1,dfn[v],sizu);
				bit.modify(l,r,valu-valv), addval(1,l,r,-(valu-valv));
		}
	}
	return iocl();
}
T4

先鸽了

上一篇:Collections


下一篇:noip模拟46