20210714 noip15

考前

mtr 中午拿着笔记本改题(Orz),一点多发现 13.50 有比赛(截止 12 点都没放出来),赶紧睡。13.40 到了学校,巨瞌睡,洗了把脸到机房发现推迟到 14.30 了,wcnm

趴在桌上睡觉,Orz 抓紧时间打 luogu 月赛的大佬

考场

T1 集体讨论得 dead line 是直线,感觉有点像《仪仗队》,打出 \(n,m\le3\) 的表发现似乎可以枚举以形成直线的两点为对角的矩形算,跳了
T2 想到了正确性不明 \(O(n\log^2 n)\) 点分治,但本人分治学的很烂就没准备写(ys 巨佬考场就写了出来,切了),想到了从大到小加点,并查集维护直径,但不知道如果直径不过当前点怎么办,跳了跳了
T3 一眼线段树求没有花精的区间中最长的,稍微划拉了一下如何 up,感觉可做,毕竟数据结构算是我的强项

怕 T3 写不完,15.20 就开始写了。
回来看 T1,先写完了假做法,发现在 \(3\times4\) 的时候就挂了,看上去要容斥。又开始尝试每次以最下、最右的点为一个端点,用欧拉函数算贡献,然后把这些点去掉,但想不清楚,最后写了暴力跑路
T2 裸暴力
大概 16.00 开始写 T3,先手推清楚了具体如何 up,问题是在线段树的每个节点上开了个结构体(重载小于号),常数巨大。17.00 写完,调了调边界就过了小样例,发现就这个题没大样例。。。只能对拍。结果没拍几组就挂了。慌得一比,调小数据发现比较区间哪个优是不能直接比区间中点到两边的距离,因为 \(0,n+1\) 两个位置上没有花精,改了改重载小于号终于在 17.20 过拍了。。。
交题时怕 T1 MLE,\(N\)\(2\times10^7\) 改到了 \(40^4\)。最后又觉得能多骗分,想着平时能开 \(5\times10^7\)int,于是开到了 \(10^7\)

res

rk1 0+55+100
T1 MLE。平时能恰好开 \(6\times10^7\)int 是因为有 256M,但这题只有 128M。。。
T2 卡常过了一些点

rk2 赵旭兵 60+30+60
rk6 ys 0+100+0

夜莺与玫瑰

枚举方向向量 \((i,j)\),显然有 \(\gcd(i,j)=1\),设这条直线与给定矩形的点的交点为 \((x,y)\),那就统计满足 \((x-i,y-j)\) 在矩形内,\((x+i,y+j)\) 不在矩形内的 \((x,y)\)(即 \((x,y)\) 是这条直线与矩形的最后一个交点,下图中蓝色)。不难算出答案为:

\[\sum_{i=1}^{n-1}\sum_{j=1}^{m-1}[\gcd(i,j)=1](n-i)(m-j)-\max(n-2i,0)\times\max(m-2j,0) \]

\[=\sum_{i=1}^{n-1}\sum_{j=1}^{m-1}[\gcd(i,j)=1](nm-im-jn+ij)+\sum_{i=1}^{n/2}\sum_{j=1}^{m/2}[\gcd(i,j)=1](-nm+2im+2jn-4ij) \]

理解:
首先 \(i,j\) 要互质,其次 \((x-i,y-j)\) 在矩形内的 \((x,y)\)\((n-i)(m-j)\) 个(黄色+蓝色),\((x+i,y+j)\) 也在矩形内的有 \((n-2i)(m-2j)\) 个(黄色),容斥一下即可。
image
由于 \(n\le4000\),可以二维前缀和预处理答案。一个问题是需要卡空间,一个大问题是数据中有 \(n,m=4001\) 的点,出题人我问候你

基于这个相同的式子,有时间 \(O(n^2+Tn)\),空间 \(O(n^2)\) 的做法
基于莫比乌斯反演,有时间 \(O(n+Tn)\),空间 \(O(n)\) 的做法。

code
#define int unsigned
const int N = 4004, mod = (1<<30)-1;
int T,n,m;

int ans[N+8][N+8],a[N+8][N+8];
bitset<N+8> is[N+8];

signed main() {
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
	is[1][0] = 1;
	For(i,1,N) For(j,1,N) is[i][j] = is[min(i,j)][max(i,j)%min(i,j)];
////////// nm项 ij项 
	For(i,1,N-1) For(j,1,N-1) if( is[i][j] ) {
		++ans[i+1][j+1], a[i+1][j+1] += i*j;
		if( i < 2e3+2 && j < 2e3+2 )
			--ans[i<<1|1][j<<1|1], a[i<<1|1][j<<1|1] -= 4*i*j;
	}
	For(i,1,N) For(j,1,N)
		ans[i][j] += ans[i-1][j] + ans[i][j-1] - ans[i-1][j-1],
		a[i][j] += a[i-1][j] + a[i][j-1] - a [i-1][j-1];
	// ans[n][m]: gcd(1..n,1..m)=1的个数(nm项的系数) 
	// a[n][m]: ij项 
	For(i,1,N) For(j,1,N) ans[i][j] = ans[i][j]*i*j + a[i][j];
////////// im项 jn项 
	memset(a,0,sizeof a);
	For(i,1,N-1) For(j,1,N-1) if( is[i][j] ) {
		a[i+1][j+1] -= i;
		if( i < 2e3+2 && j < 2e3+2 ) a[i<<1|1][j<<1|1] += i<<1;
	}
	For(i,1,N) For(j,1,N)
		a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1],
		// a[n][m]: i in [1,n],j in [1,m],gcd(i,j)=1的i的和 
		ans[i][j] += j*a[i][j]; // 此处的j为式子中的m 
	memset(a,0,sizeof a);
	For(i,1,N-1) For(j,1,N-1) if( is[i][j] ) {
		a[i+1][j+1] -= j;
		if( i < 2e3+2 && j < 2e3+2 ) a[i<<1|1][j<<1|1] += j<<1;
	} 
	For(i,1,N) For(j,1,N)
		a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1],
		ans[i][j] += i*a[i][j];
	read(T);
	while( T-- ) {
		read(n,m);
		// assert(n<=4000),assert(m<=4000);
		write((n+m+2*ans[n][m])&mod);
	}
	return ioclear();
}

影子

考场上其实想的差不多

每次合并完并查集后,用当前点点权乘上直径更新答案即可。因为是用当前点来合并连通块,因此如果直径的两个端点在原来的联通块中,那么这次的答案一定不比那时算出来的优(点权不会变大);否则直径一定会过当前点

code
const int N = 1e5+5;
int T,n,mm,val[N],head[N];
struct Edge { int to,w,nxt; } e[N*2];

int id[N];
struct DSU {
	int fa,p,q;
	LL d;
} s[N];

namespace dist {
int fa[N],dep[N],siz[N],son[N],top[N];
LL d[N];
void dfs1(int u,int f) {
	dep[u] = dep[ fa[u]=f ]+1, siz[u] = 1, son[u] = 0;
	for(int i = head[u], v; i; i = e[i].nxt) if( (v=e[i].to) != f ) {
		d[v] = d[u] + e[i].w;
		dfs1(v,u);
		siz[u] += siz[v];
		if( siz[v] > siz[son[u]] ) son[u] = v;
	}
}
void dfs2(int u,int t) {
	top[u] = t;
	if( son[u] ) dfs2(son[u],t);
	for(int i = head[u], v; i; i = e[i].nxt)
		if( (v=e[i].to) != fa[u] && v != son[u] ) dfs2(v,v);
}
void init() { dfs1(1,0), dfs2(1,1); }
int lca(int u,int v) {
	while( top[u] != top[v] ) {
		if( dep[top[u]] < dep[top[v]] ) swap(u,v);
		u = fa[top[u]];
	}
	return dep[u]<dep[v] ? u : v;
}
LL dis(int u,int v) { return d[u]+d[v]-d[lca(u,v)]*2; }
}
using dist::dis;

int find(int x) { return s[x].fa==x ? x : s[x].fa=find(s[x].fa); }
void up(int x,int p,int q) {
	LL d = dis(p,q);
	if( d > s[x].d ) s[x].p = p, s[x].q = q, s[x].d = d;
}
void merge(int x,int y) {
	if( x == y ) return;
	s[y].fa = x;
	int p = s[x].p, q = s[x].q;
	up(x,s[y].p,s[y].q);
	up(x,p,s[y].p), up(x,p,s[y].q), up(x,q,s[y].p), up(x,q,s[y].q);
}

void solve() {
	LL ans = 0;
	read(n);
	For(i,1,n) read(val[i]), id[i] = i, s[i] = (DSU){i,i,i,0};
	for(int i = 1; i < n; ++i) {
		int x,y,z; read(x,y,z);
		e[++mm] = (Edge){y,z,head[x]}, head[x] = mm;
		e[++mm] = (Edge){x,z,head[y]}, head[y] = mm;
	}
	dist::init();
	sort(id+1,id+n+1,[](const int &x,const int &y){return val[x]>val[y];});
	For(i,1,n) {
		int u = id[i];
		for(int j = head[u], v; j; j = e[j].nxt)
			if( val[ v=e[j].to ] >= val[u] ) merge(find(u),find(v));
		ans = max(ans,val[u]*s[find(u)].d);
	}
	write(ans,10);
}

signed main() {
	read(T);
	while( T-- ) {
		mm = 1;
		mem(head,0,n);
		solve();
	}
	return ioclear();
}

点分治做法(虽然时间、代码复杂度被爆踩)

玫瑰花精

线段树

考场 code
const int N = 2e5+5, M = 1e6+5;
int n,m;

int pos[M];
struct Seg {
	int l,r;
	int len() {
		if( l == 1 ) return r;
		if( r == n ) return n-l+1;
		return (l+r>>1)-l+1;
	}
	Seg(int l=0,int r=0):l(l),r(r){}
};
bool operator < (Seg x,Seg y)
	{ return x.len()!=y.len() ? x.len()>y.len() : x.l<y.l; }

struct Node {
	int l,r,lr,rl;
	// lr: 从左往右最后一个没占的地方
	Seg a;
} t[N*4];
void up(int u) {
	int ls = u<<1, rs = u<<1|1;
	if( t[ls].lr == t[ls].r && t[rs].lr ) t[u].lr = t[rs].lr;
	else t[u].lr = t[ls].lr;
	if( t[rs].rl == t[rs].l && t[ls].rl <= n ) t[u].rl = t[ls].rl;
	else t[u].rl = t[rs].rl;
	t[u].a = min(t[ls].a,t[rs].a);
	t[u].a = min(t[u].a,Seg(min(t[ls].rl,t[rs].l),max(t[rs].lr,t[ls].r)));
}
void build(int u,int l,int r) {
	t[u].l = l, t[u].r = r;
	if( l == r ) {
		t[u].lr = t[u].rl = l;
		t[u].a = Seg(l,l);
		return;
	}
	int mid = l+r>>1;
	build(u<<1,l,mid), build(u<<1|1,mid+1,r);
	up(u);
//	printf("> %d %d: %d %d %d %d\n",l,r,t[u].lr,t[u].rl,t[u].a.l,t[u].a.r);
}
void modify(int u,int p,bool x) {
	if( t[u].l == t[u].r ) {
		if( x ) t[u].lr = 0, t[u].rl = n+1, t[u].a = Seg(n+1,0);
		else t[u].lr = t[u].rl = t[u].l, t[u].a = Seg(t[u].l,t[u].l);
		return;
	}
	int ls = u<<1, rs = ls|1;
	modify( p<=t[ls].r?ls:rs ,p,x);
	up(u);
}

signed main() {
	// freopen("c.in","r",stdin);
	// freopen("c.out","w",stdout);
	read(n,m);
	build(1,1,n);
	while( m-- ) {
		int op,x; read(op,x);
		if( op == 1 ) {
			Seg a = t[1].a;
			if( a.l == 1 ) pos[x] = 1;
			else if( a.r == n ) pos[x] = n;
			else pos[x] = a.l+a.r>>1;
			modify(1,pos[x],1);
			write(pos[x]), putc(10);
		} else modify(1,pos[x],0);
	}
	return ioclear();
}
posted @ 2021-07-15 12:06  401rk8  阅读(91)  评论(0编辑  收藏  举报