20210818 noip43

考场

T1 这不 sb 题。尝试写对拍发现自己不会打暴力。
T3 枚举二进制中 \(1\) 的个数,数位 DP 就完了。写了写发现不会求第 \(k\) 大,死了。
T2 盯着 excel 看的时候突然想到二分答案,写了个 shuffle + 类似 SPFA 的东西
T4 裸暴力

res

rk8 30+100+10+10
T1 做法假了(貌似和 ys 写的一样)

rk1 szs 100+90+40+100
rk2 马瑞暄 100+100+10+0
rk3 ycx 100+40+40+10
rk6 hkh 100+40+10+10
rk8 wcr 100+40+10+0

总结

算是有些进步吧,考场上想出了昨天 T4,今天 T3 的正解,虽然没写出来,但赛后都在赛时代码上改改就 A 了。两题考场上都有考虑不周/不会实现的问题,不是调了很久就是写到一半不会了,说明思维还是不够严密,不够深入,想到一个做法没能理性判断它可不可行,靠感觉太多了。

码力太弱。一些板子(如 manacher)虽然能写出来,但太不熟练,考场上浪费时间,这部分要多刷题;改 T4 时边界一直不对,最后看了 ycx 才过,这与在 sdfz 改题时在网上搜题解,大概懂了就开始抄代码有关,趁在 hz 开网晚,多思考实现细节,自己改出来。
改题时不要把下发题解当作题解,它不配,当作提示就好,尽量自己想。如果能自己改出来更好。

太依赖对拍,对算法的正确性没有清楚认知,假做法一旦拍不出来自己也发现不了。大概率是平时做题只求会做,不关注正确性和复杂度分析的习惯导致的,尽快改正吧。

sol

T1

贪心。如果存在一个人从叶子节点向上走的情况,那么一定是从较浅的叶子走到较深的

const int N = 1e5+5;
int n;
vector<int> to[N];

int dep[N],mxdep[N],siz[N];
LL ans;

void dfs(int u,int fa) {
	mxdep[u] = dep[u], siz[u] = 1;
	for(int v : to[u]) if( v != fa ) {
		dep[v] = dep[u]+1;
		dfs(v,u);
		siz[u] += siz[v];
		ckmax(mxdep[u],mxdep[v]);
	}
}
void dfs1(int u,int fa) {
	bool flg = 1;
	for(int v : to[u]) if( v != fa ) {
		if( mxdep[v]-dep[u] <= dep[u] ) ans += siz[v]*2;
		else flg = 0;
	}
	if( flg ) return ans -= mxdep[u]-dep[u], void();
	ans -= dep[u];
	for(int v : to[u]) if( v != fa && mxdep[v]-dep[u] > dep[u] )
		ans += dep[v], dfs1(v,u); 
}

signed main() {
	read(n);
	For(i,1,n-1) { int x,y; read(x,y); to[x].pb(y), to[y].pb(x); }
	dfs(1,0), dfs1(1,0);
	write(ans);
	return iocl();
}
T2

貌似朴素最短路被卡常了。需要队列模拟堆(dij)或者随机化(SPFA)

考场代码

mt19937 mt(time(0));

const int N = 1e5+5, dx[]={1,1,-1,0,-1,0}, dy[]={-1,0,0,-1,1,1};
int n,m;
LL k;
vector<LL> a[N];

int idx[N],idy[N];
vector<bool> vis[N];
vector<LL> b[N];
queue<PII> que;

LL calc(int x,int y) {
	LL res = 0;
	For(i,0,5) {
		int xx = x+dx[i], yy = y+dy[i];
		if( xx<1||xx>n||yy<1||yy>m ) continue;
		ckmax(res,b[xx][yy]-b[x][y]);
	}
	return res;
}
bool check(LL lim) {
	LL use = 0;
	For(i,1,n) b[i] = a[i], vis[i].assign(m+1,0);
	while(!que.empty())que.pop();
	For(i,1,n) {
		int x = idx[i];
		For(j,1,m) {
			int y = idy[j];
			if( calc(x,y) > lim ) que.push(MP(x,y)), vis[x][y] = 1;
		}
	}
	while( !que.empty() ) {
		int x = que.front().fi, y = que.front().se; que.pop();
		vis[x][y] = 0;
//		cerr<<x<<','<<y<<':'<<b[x][y]<<endl;
		LL del = calc(x,y)-lim;
		b[x][y] += del, use += del;
		For(i,0,5) {
			int xx = x+dx[i], yy = y+dy[i];
			if( xx<1||xx>n||yy<1||yy>m ) continue;
			if( b[x][y]-b[xx][yy] > lim && !vis[xx][yy] )
				que.push(MP(xx,yy)), vis[xx][yy] = 1;
		}
		if( use > k ) return 0;
	}
	return 1;
}

signed main() {
//	freopen("b.in","r",stdin);
//	freopen("b.out","w",stdout);
	read(n,m,k);
	For(i,1,n) idx[i] = i; shuffle(idx+1,idx+n+1,mt);
	For(i,1,m) idy[i] = i; shuffle(idy+1,idy+m+1,mt);
	For(i,1,n) {
		a[i].assign(m+1,0), b[i].assign(m+1,0);
		For(j,1,m) read(a[i][j]);
	}
	LL l = 0, r = 1e12;
	while( l < r ) {
		LL mid = l+r>>1;
		if( check(mid) ) r = mid;
		else l = mid+1;
	}
	write(l);
	return iocl();
}
T3

按二进制中 \(1\) 的个数从多到少考虑,如果不能全选就在二进制位上二分。(类似线段树二分)

考场代码上改的,比较丑陋

typedef pair<int,LL> PIL;
PIL operator + (PIL x,PIL y) { return MP(x.fi+y.fi,x.se+y.se); }

const int N = 31;
int T,l,r,a,b;
int len,n,dl[N],dr[N];
LL ans;
PIL f[N][N];

PIL dfs(int u,bool liml,bool limr,int num) {
	if( num < 0 ) return MP(0,0);
	if( !u ) return MP(!num,0);
	if( !liml && !limr && ~f[u][num].fi ) return f[u][num];
	PIL res = MP(0,0);
	if( !limr || dr[u] )
		res = dfs(u-1,liml&&dl[u],limr,num-1), res.se += res.fi * (1ll<<u-1);
	if( !liml || !dl[u] ) res = res + dfs(u-1,liml,limr&&!dr[u],num);
	if( !liml && !limr ) f[u][num] = res;
	return res;
}
void dfs1(int u,bool liml,bool limr,int num) {
	if( !n || num < 0 ) return;
	if( !limr || dr[u] ) {
		PIL res = dfs(u-1,liml&&dl[u],limr,num-1);
		if( res.fi <= n ) n -= res.fi, ans += res.se + res.fi * (1ll<<u-1);
		else ans += n*(1ll<<u-1), dfs1(u-1,liml&&dl[u],limr,num-1);
	}
	if( !liml || !dl[u] ) dfs1(u-1,liml,limr&&!dr[u],num);
}
LL solve(int m) {
	n = m, len = ans = 0;
	for(int i = l, j = r; j; j >>= 1, i >>= 1) dl[++len] = i&1, dr[len] = j&1;
	rFor(i,len,0) {
		PIL res = dfs(len,1,1,i);
//		cerr<<i<<": "<<res.fi<<' '<<res.se<<endl;
		if( res.fi <= n ) n -= res.fi, ans += res.se;
		else { dfs1(len,1,1,i); break; }
	}
//	cerr<<m<<' '<<ans<<endl;
	return ans;
}

signed main() {
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	memset(f,0xff,sizeof f);
	read(T);
	while( T-- ) {
		read(l,r,a,b);
		write(solve(b)-solve(a-1));
	}
	return iocl();
}
T4

DP。设 \(f[i,j]\) 为前 \(i\) 个数中最大值为 \(j\) 的方案数,\(g[i,j]\) 为在最大值为 \(j\) 的数列后加 \(i\) 个数的方案数,统计答案时拼起来。

const int N = 3e3+5;
int n,mod;

LL f[N][N],g[N][N],h[N][N];

signed main() {
	read(n,mod);
	f[0][0] = 1;
	For(i,1,n) For(j,1,i) f[i][j] = (f[i-1][j] * j + f[i-1][j-1]) %mod;
	For(i,0,n) g[0][i] = 1;
	For(i,1,n) For(j,1,n-1) g[i][j] = (g[i-1][j] * j + g[i-1][j+1]) %mod;
	For(j,1,n) For(k,1,n) {
		h[j][k] = f[j-1][k] * g[n-j][k] %mod;
		if( j < n )
			h[j][k] = (h[j][k] + f[j-1][k] * (2*(n-j)*g[n-j-1][k]%mod)) %mod;
		h[j][k] = (h[j][k] + h[j][k-1]) %mod;
	}
	For(i,1,n) {
		LL ans = 0;
		For(j,1,n) {
			ans += f[j-1][i-1] * g[n-j][i] %mod;
			if( j < n ) ans += f[j-1][i-1] * (2*(n-j)*g[n-j-1][i]%mod) %mod;
			ans = (ans + h[j][n]-h[j][i-1]+mod) %mod;
		}
		write(ans%mod,' ');
	}
	return iocl();
}

T1 考场做法

\(f[i,0/1]\) 表示走完子树 \(i\) 后没有/有士兵走回来,DP。

code
#include <bits/stdc++.h>
using namespace std;
#define For(i,x,y) for(int i=x;i<=y;++i)
#define rFor(i,x,y) for(int i=x;i>=y;--i)
#define mem(a,x,n) memset(a,x,sizeof(a[0])*(n+1))
#define pb emplace_back
#define MP make_pair
#define fi first
#define se second
#define FF fflush(stdout)
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
typedef pair<int,int> PII;
char buf[1<<22],*p1=buf,*p2=buf,pbuf[1<<22],*pp=pbuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
#define iocl() fwrite(pbuf,1,pp-pbuf,stdout),pp=pbuf,0
#define putchar(x) pp-pbuf==1<<22&&(iocl()),*pp++=x
template<typename T>void read(T &x){
	x=0;bool f=1;char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=0;
	if(f)for(;isdigit(c);c=getchar())x=x*10+c-48;
	else for(;isdigit(c);c=getchar())x=x*10-c+48;
}
template<typename T,typename ...Args>void read(T &x, Args &...args)
	{ read(x),read(args...); }
template<typename T>void write(T x,char y=10) {
	if(!x)putchar(48);
	else{static int s[21];int l=0;if(x<0)putchar('-'),x=-x;
		for(;x;x/=10)s[l++]=x%10;while(l)putchar(s[--l]|48);}
	putchar(y);
}
template<typename T>void ckmax(T &x,T y) { if( y > x ) x = y; }
template<typename T>void ckmin(T &x,T y) { if( y < x ) x = y; }

const int N = 1e5+5;
int n;
vector<int> to[N];

int dep[N],mxdep[N];
LL f[N][2];

void dfs(int u,int fa) {
	mxdep[u] = dep[u];
	for(int v : to[u]) if( v != fa ) {
		dep[v] = dep[u]+1;
		dfs(v,u);
		ckmax(mxdep[u],dep[v]);
	}
}
void dp(int u,int fa) {
	sort(to[u].begin(),to[u].end(),
		[](const int &x,const int &y){return mxdep[x]<mxdep[y];});
	for(int v : to[u]) if( v != fa ) {
		dp(v,u);
		int x = f[u][0], y = f[u][1];
		f[u][0] = min(y+f[v][0]+1,x+dep[v]+f[v][0]);
		f[u][1] = min(y+f[v][1]+2,x+dep[v]+f[v][1]);
	}
}

signed main() {
	read(n);
	For(i,1,n-1) { int x,y; read(x,y); to[x].pb(y), to[y].pb(x); }
	dfs(1,0), dp(1,0);
	write(min(f[1][0],f[1][1]));
	return iocl();
}

upd:又考了一次

sol

考场编的贪心
const int N = 1e6+5;
int n,fa[N];
VI to[N];

int dep[N],mxd[N],mnd[N];
LL ans;

void dfs(int u) {
	mxd[u] = dep[u];
	for(int v : to[u]) dep[v] = dep[u]+1, dfs(v), ckmax(mxd[u],mxd[v]);
	sort(to[u].begin(),to[u].end(),
		[](const int &x,const int &y){return mxd[x]<mxd[y];});
}
void dp(int u) {
	if( !to[u].size() ) return mnd[u] = dep[u], void();
	++ans, dp(to[u][0]), mnd[u] = mnd[to[u][0]];
	For(i,1,to[u].size()-1) { int v = to[u][i];
		ans += min(dep[v],mnd[u]-dep[u]+1), dp(v), mnd[u] = mnd[v];
	}
}

signed main() {
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	read(n); For(i,2,n) read(fa[i]), to[fa[i]].pb(i);
	dfs(1), dp(1);
	write(ans);
	return iocl();
}
posted @ 2021-08-19 06:36  401rk8  阅读(59)  评论(2编辑  收藏  举报