加载中…

返回上一页

2022NOIP A层联测33

声明:本文为博客园 1Liu 原创文章. 如果您看到此声明一般是该内容由其它网站通过脚本获取以骗取流量,为保证体验请至原文查看. 原文地址:https://www.cnblogs.com/1Liu/p/16915890.html

下发文件(密码为原 accoders 比赛密码)

今天的比赛,因为就想优化一下复杂度,结果因为错误地预计了复杂度收获了 3 个题保龄的好成绩.(

GCD

枚举每个 gcd,然后枚举它的倍数,拿一个 set 和并查集维护.

点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 300001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,m,q,ans[maxn],fa[maxn],ls[maxn];
vector<pll> g[maxn],a[maxn];
set<ll> s;
// inline ll gcd(rll x,rll y) { if(!y) return x; return gcd(y,x%y); }
inline ll find(rll x) { if(x^fa[x]) fa[x]=find(fa[x]); return fa[x]; }
int main()
{
	freopen("gcd.in","r",stdin); freopen("gcd.out","w",stdout);
	memset(ans,0b11111111,sizeof(ans));
	for(rll i=1;i<maxn;i++) fa[i]=i;// fa[2]=4;fa[4]=2;find(2);
	n=read();m=read();q=read(); for(rll i=1,u,v;i<=m;i++) u=read(),v=read(),g[read()].emplace_back(u,v);
	for(rll i=1,x;i<=q;i++) x=read(),a[x].emplace_back(read(),i);
	for(rll i=1;i<maxn;i++)
	{
		s.clear(); for(rll j=1;i*j<maxn;j++) if(!g[i*j].empty()) s.insert(g[i*j][0].first),s.insert(g[i*j][0].second);
		for(rg auto j:s) fa[j]=j; for(rll j=1;i*j<maxn;j++) if(!g[i*j].empty()) fa[find(g[i*j][0].first)]=find(g[i*j][0].second);
		for(rg auto j:s) for(rll k=0;k<a[j].size();k++) if(s.count(a[j][k].first)&&find(j)==find(a[j][k].first)) ans[a[j][k].second]=i;
	}
	for(rll i=1;i<=q;i++) write(ans[i]),putn;
	return 0;
}

简单题

发现数对中 y 最多只会迭代 20 次,因此维护一棵线段树,在 y 还不是 1 的情况下,就暴力修改,否则直接区间对 x 加 1.

赛时就是害怕 TLE,结果改成了用 basic_string 进行区间修改,结果可想而知全 T 了.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 1000001
#define mod 998244353
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
struct tree
{
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
	ll x,y,tag,sum;
}t[maxn<<2];
ll n,m,l,r,p[maxn],cnt,phi[maxn],x[maxn],y[maxn];
pll ans;
bool fl[maxn];
inline void shai()
{
	phi[1]=1;
	for(rll i=2;i<maxn;i++)
	{
		if(!fl[i]) p[++cnt]=i,phi[i]=i-1;
		for(rll j=1;j<=cnt&&i*p[j]<maxn;j++)
		{
			fl[i*p[j]]=1; if(!(i%p[j])) { phi[i*p[j]]=phi[i]*p[j]; break; }
			phi[i*p[j]]=phi[i]*phi[p[j]];
		}
	}
	// for(rll i=1;i<=200;i++) write(i),put_,write(phi[i]),putn;
}
inline void pushdown(rll rt,rll l,rll r)
{
	rll mid=l+r>>1; if(t[rt].tag)
		t[ls(rt)].tag+=t[rt].tag,t[rs(rt)].tag+=t[rt].tag,
		(t[ls(rt)].x+=t[rt].tag*(mid-l+1))%=mod,(t[rs(rt)].x+=t[rt].tag*(r-mid))%=mod,t[rt].tag=0;
}
inline void pushup(rll rt)
{
	t[rt].x=(t[ls(rt)].x+t[rs(rt)].x)%mod,t[rt].y=(t[ls(rt)].y+t[rs(rt)].y);
}
inline void build(rll rt,rll l,rll r)
{
	if(l==r) { t[rt].x=x[l],t[rt].y=y[l]; return; } rll mid=l+r>>1;
	build(ls(rt),l,mid);build(rs(rt),mid+1,r); pushup(rt);
}
inline void upd(rll rt,rll l,rll r,rll x,rll y)
{
	if(x<=l&&r<=y) { if(t[rt].y==r-l+1) { t[rt].x+=r-l+1; t[rt].tag++; return; } if(l==r) { t[rt].x+=t[rt].y,t[rt].y=phi[t[rt].y]; return; } }
	pushdown(rt,l,r); rll mid=l+r>>1; if(x<=mid) upd(ls(rt),l,mid,x,y); if(y>mid) upd(rs(rt),mid+1,r,x,y); pushup(rt);
}
inline void operator+=(rg pll& a,rg pll b) { (a.first+=b.first)%=mod,(a.second+=b.second)%=mod; }
inline pll query(rll rt,rll l,rll r,rll x,rll y)
{
	if(x<=l&&r<=y) return (pll){ t[rt].x%mod,t[rt].y }; pushdown(rt,l,r);rll mid=l+r>>1;rg pll ans={ 0,0 };
	if(x<=mid) ans=query(ls(rt),l,mid,x,y); if(y>mid) ans+=query(rs(rt),mid+1,r,x,y); return ans;
}
int main()
{
	freopen("simple.in","r",stdin); freopen("simple.out","w",stdout);
	shai(); n=read();m=read(); for(rll i=1;i<=n;i++) x[i]=read(),y[i]=read(); build(1,1,n);
	while(m--) switch(read())
	{
	case 1:l=read();r=read();upd(1,1,n,l,r);break;
	case 2:l=read();r=read();ans=query(1,1,n,l,r);write((ans.first%mod+mod)%mod),put_,write((ans.second%mod+mod)%mod),putn;
	}
	return 0;
}

建筑

对于一个矩形,它的答案显然是 \max a_{x,y}\times \text{size}(x_1,y_1\sim x_2,y_2)-\sum\limits_{i=x_1,j=y_1}^{i=x_2,j=y_2}a_{i,j}.

为了减小复杂度,我们去枚举较短一边的区间,然后去统计较长一边的区间.

首先枚举列的起与止. 对于这些列,我们找出每一列中所有行的权值的最大值. 然后对每一列,找到它所管辖到最远的位置(就是到哪里它都是最大值).

对于每个点,它的贡献就是一个类似滑块的东西,只需要包含这个点的所有区间.

如何计算?

先不考虑减去的那一部分,对于这个点的左边和右边,分开考虑.

设起到左边的点的总数为 x(不含这个点本身),到右边的点的总数为 y(同样不含).

对于左边半部分(中间端点)里的每个点,其右边都有从 1 到 y 的右端点,总和起来的贡献就是 \frac{y(y+1)}{2}. 那么总共有 (x + 1) 个点(因为包括中间点),总计 \frac{(x+1)y(y+1)}{2}. 这样就是右半部分对左半部分的贡献.

对于左半部分对左半部分的贡献,因为每个数都会出现 y + 1 次(因为中间端点自己还要算一次),一共 x + 1 个数(因为中间点自己到自己这个点当左端点和右端点都是合法的),答案为 \frac{(x+1)(x+2)(y+1)}{2}.

加起来即可.

对于多的部分,最后用类似的方法减去.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 100001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,m,ans,mx[maxn],ls[maxn],nx[maxn];
vector<ll> a[maxn];
int main()
{
	freopen("building.in","r",stdin); freopen("building.out","w",stdout);
	n=read();m=read();
	if(n>m) { for(rll i=1;i<=m;i++) a[i].emplace_back(0); for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) a[j].emplace_back(read()); swap(n,m); }
	else { for(rll i=1;i<=m;i++) a[i].emplace_back(0); for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) a[i].emplace_back(read()); }
	for(rll l=1;l<=n;l++)
	{
		memset(mx,0,sizeof(mx));
		for(rll r=l;r<=n;r++)
		{
			for(rll i=1;i<=m;i++) mx[i]=max(mx[i],a[r][i]);
			for(rll i=1;i<=m;i++) { ls[i]=i; while((ls[i]^1)&&mx[i]>mx[ls[i]-1]) ls[i]=ls[ls[i]-1]; }
			for(rll i=m;i;i--) { nx[i]=i; while((nx[i]^m)&&mx[i]>=mx[nx[i]+1]) nx[i]=nx[nx[i]+1]; }
			for(rll i=1,x,y;i<=m;i++) x=i-ls[i],y=nx[i]-i,ans+=(r-l+1)*(((y+1)*(x+1)*(x+2)>>1)+((x+1)*y*(y+1)>>1))*mx[i];
		}//  
	}
	for(rll i=1;i<=n;i++) for(rll j=1;j<=m;j++) ans-=a[i][j]*i*(n-i+1)*j*(m-j+1); write(ans);
	return 0;
}

树上前缀和

lca 模板题.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 100001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,m,x,y,l,sum[maxn],a[maxn],f[maxn],d[maxn],sz[maxn],son[maxn],dfn[maxn],top[maxn],cnt;
vector<ll> g[maxn];
inline void dfs1(rll x,rll fa)
{
	f[x]=fa;d[x]=d[fa]+1;sz[x]=1;son[x]=0;sum[x]=sum[fa]+a[x];
	for(rll i=0;i<g[x].size();i++)
	{
		rll to=g[x][i]; if(to==fa) continue; dfs1(to,x); sz[x]+=sz[to]; if(sz[to]>sz[son[x]]) son[x]=to;
	}
}
inline void dfs2(rll x,rll fa)
{
	dfn[x]=++cnt;top[x]=fa;if(son[x]) dfs2(son[x],fa);
	for(rll i=0;i<g[x].size();i++) { rll to=g[x][i]; if(dfn[to]) continue; dfs2(to,to); }
}
inline ll lca(rll x,rll y)
{
	while(top[x]^top[y]) { if(d[top[y]]>d[top[x]]) swap(x,y); x=f[top[x]]; }
	if(d[x]<d[y]) return x; return y;
}
int main()
{
	freopen("shuqianzhui.in","r",stdin); freopen("shuqianzhui.out","w",stdout);
	n=read();m=read(); for(rll i=1;i<=n;i++) a[i]=read();
	for(rll i=1,u,v,w;i<n;i++) u=read(),v=read(),g[u].emplace_back(v),g[v].emplace_back(u);
	dfs1(1,0);dfs2(1,1);
	while(m--)
	{
		x=read();y=read();l=lca(x,y); write(sum[x]+sum[y]-(sum[l]<<1)+a[l]);putn;
	}
	return 0;
}
posted @ 2022-11-22 17:35  1Liu  阅读(48)  评论(1编辑  收藏  举报