AtCoder 选做

ARC063F

https://atcoder.jp/contests/arc063/tasks/arc063_d

因为都是整点,注意到答案一定会大于等于 \(2\times \max\{H+1,W+1\}\) 所以答案矩形一定有一边大于 \(H/2\)\(W/2\),即至少过大矩形的一条直的对称轴。

考虑答案矩形过 \(y=H/2\) 的情况,用扫描线从左往右扫,算出每个点向上向下最远能延伸到多远。用单调增的单调栈维护上下的点,用线段树维护以扫描线为右边界,左侧各点为左边界的最大周长。过 \(x=W/2\) 的同理。就可以做到 \(O(n\log n)\)

但具体实现细节有点多,相当不好写,最后几乎是对着题解写的

#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
inline int read(){
	int x=0; char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x;
}
const int maxn=3e5+10;
struct SegTree{
	int l,r,dat,tag;
}tr[maxn*4];
inline void pushup(int x){
	tr[x].dat=max(tr[x<<1].dat,tr[x<<1|1].dat);
}
inline void pushdown(int x){
	if(tr[x].tag){
		tr[x<<1].dat+=tr[x].tag;
		tr[x<<1].tag+=tr[x].tag;
		tr[x<<1|1].dat+=tr[x].tag;
		tr[x<<1|1].tag+=tr[x].tag;
		tr[x].tag=0;
	}
}
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r,tr[p].dat=tr[p].tag=0;
	if(l==r) return;
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}
void modify(int p,int l,int r,int v){
	if(l<=tr[p].l&&r>=tr[p].r){
		tr[p].dat+=v,tr[p].tag+=v;
		return;
	}
	pushdown(p);
	int mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) modify(p<<1,l,r,v);
	if(r>mid) modify(p<<1|1,l,r,v);
	pushup(p);
}
int query(int p,int l,int r){
	if(l<=tr[p].l&&r>=tr[p].r) return tr[p].dat;
	pushdown(p);
	int mid=tr[p].l+tr[p].r>>1;
	int res=0;
	if(l<=mid) res=query(p<<1,l,r);
	if(r>mid) res=max(res,query(p<<1|1,l,r));
	pushup(p);
	return res;
}
struct Point{
	int x,y;
	bool operator<(const Point &B)const{
		return x<B.x;
	}
}p[maxn];
int w,h,n,x[maxn],up[maxn],down[maxn],stku[maxn],stkd[maxn],top1,top2;
int solve(){
	int ans=0;
	sort(p+1,p+n+1);
	p[0].x=0,p[n+1].x=w;
	for(int i=1;i<=n+1;i++) ans=max(ans,h+p[i].x-p[i-1].x);
	build(1,1,n);
	top1=top2=0;
	for(int i=1,now=1;i<=n;now++){
		x[now]=p[i].x,up[now]=h-h/2,down[now]=h/2;
		while(i<=n&&p[i].x==x[now]){
			if(p[i].y>=h/2) up[now]=min(up[now],p[i].y-h/2);
			if(p[i].y<=h/2) down[now]=min(down[now],h/2-p[i].y);
			i++;
		}
		while(up[stku[top1]]>up[now]){
			int t=stku[top1--];
			modify(1,stku[top1]+1,t,up[now]-up[t]);
		}
		while(down[stkd[top2]]>down[now]){
			int t=stkd[top2--];
			modify(1,stkd[top2]+1,t,down[now]-down[t]);
		}
		stku[++top1]=now,stkd[++top2]=now;
		modify(1,now,now,w-x[now-1]+up[now]+down[now]);
		ans=max(ans,query(1,1,now)-w+p[i].x);
	}
	return ans;
}
int main(){
	w=read(),h=read(),n=read();
	for(int i=1;i<=n;i++) p[i].x=read(),p[i].y=read();
	int ans=solve();
	for(int i=1;i<=n;i++) swap(p[i].x,p[i].y);
	swap(w,h);
	ans=max(ans,solve());
	printf("%d\n",ans*2);
	return 0;
}

AGC006C

https://atcoder.jp/contests/agc006/tasks/agc006_c

\(a_i\) 经过一次跳跃坐标由 \(x_{a_i}\) 变为 \(x_{a_i+1}+x_{a_i-1}-x_{a_i}\),这里需要敏锐的观察到坐标的差分序列恰好是临项交换,于是 \(m\) 次跳跃对于差分序列相当于一个置换,\(k\) 轮的情况就可以快速幂搞了。

要开 \(\texttt{long long}\)

#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int n,m,x[maxn],b[maxn],pos[maxn],tmp[maxn],ans[maxn];
ll k,s[maxn];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&x[i]);
	for(int i=1;i<=n;i++) b[i]=x[i]-x[i-1];
	scanf("%d%lld",&m,&k);
	for(int i=1;i<=n;i++) pos[i]=i;
	for(int i=1;i<=m;i++){
		int a; scanf("%d",&a);
		swap(pos[a],pos[a+1]);
	}
	for(int i=1;i<=n;i++) ans[i]=i;
	for(;k;k>>=1){
		if(k&1){
			for(int i=1;i<=n;i++) tmp[i]=pos[ans[i]];
			for(int i=1;i<=n;i++) ans[i]=tmp[i];
		}
		for(int i=1;i<=n;i++) tmp[i]=pos[pos[i]];
		for(int i=1;i<=n;i++) pos[i]=tmp[i];
	}
	for(int i=1;i<=n;i++) s[i]=s[i-1]+b[ans[i]];
	for(int i=1;i<=n;i++) printf("%lld.0\n",s[i]);
	return 0;
}

AGC014E

https://atcoder.jp/contests/agc014/tasks/agc014_e

树剖就可以 \(O(n\log^2n)\) 做。

一个很好的技巧是维护覆盖蓝链的每条红边的编号异或和,这样只被一条红边覆盖时就方便查询是哪条红边。

一遍 AC,太不容易了,虽然树剖是照着板子打的

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
using namespace std;
const int INF=1e6;
const int maxn=1e5+10;
inline int read(){
	int x=0; char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x;
}
struct Edge{ int to,next;}edge[maxn*2];
int head[maxn],cnt;
inline void addedge(int u,int v){
	edge[++cnt].to=v,edge[cnt].next=head[u],head[u]=cnt;
}
pair<int,int> red_e[maxn];
int n,dep[maxn],fa[maxn],son[maxn],siz[maxn];
void dfs1(int x){
	siz[x]=1;
	for(int i=head[x];i;i=edge[i].next){
		int y=edge[i].to;
		if(y==fa[x]) continue;
		fa[y]=x;
		dep[y]=dep[x]+1;
		dfs1(y);
		if(siz[y]>siz[son[x]]) son[x]=y;
		siz[x]+=siz[y];
	}
}
int top[maxn],dfn[maxn],pos[maxn],idx;
void dfs2(int x){
	dfn[x]=++idx,pos[idx]=x;
	if(!son[x]) return;
	top[son[x]]=top[x];
	dfs2(son[x]);
	for(int i=head[x];i;i=edge[i].next){
		int y=edge[i].to;
		if(dfn[y]) continue;
		top[y]=y;
		dfs2(y); 
	}
}
struct SegTree{
	int l,r,val,dat,loc,tag_x,tag_a;
}tr[maxn*4];
inline void pushup(int x){
	tr[x].dat=min(tr[x<<1].dat,tr[x<<1|1].dat);
	tr[x].loc=tr[x].dat==tr[x<<1].dat?tr[x<<1].loc:tr[x<<1|1].loc;
}
inline void pushdown(int x){
	if(tr[x].tag_a){
		tr[x<<1].dat+=tr[x].tag_a;
		tr[x<<1].tag_a+=tr[x].tag_a;
		tr[x<<1|1].dat+=tr[x].tag_a;
		tr[x<<1|1].tag_a+=tr[x].tag_a;
		tr[x].tag_a=0;
	}
	if(tr[x].tag_x){
		tr[x<<1].val^=tr[x].tag_x;
		tr[x<<1].tag_x^=tr[x].tag_x;
		tr[x<<1|1].val^=tr[x].tag_x;
		tr[x<<1|1].tag_x^=tr[x].tag_x;
		tr[x].tag_x=0;
	}
}
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r,tr[p].loc=l;
	if(l==r) return;
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}
void m_xor(int p,int l,int r,int v){
	if(l<=tr[p].l&&r>=tr[p].r){
		tr[p].val^=v,tr[p].tag_x^=v;
		return;
	}
	pushdown(p);
	int mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) m_xor(p<<1,l,r,v);
	if(r>mid) m_xor(p<<1|1,l,r,v);
	pushup(p);
}
void m_add(int p,int l,int r,int v){
	if(l<=tr[p].l&&r>=tr[p].r){
		tr[p].dat+=v,tr[p].tag_a+=v;
		return;
	}
	pushdown(p);
	int mid=tr[p].l+tr[p].r>>1;
	if(l<=mid) m_add(p<<1,l,r,v);
	if(r>mid) m_add(p<<1|1,l,r,v);
	pushup(p);
}
int q_xor(int p,int x){
	if(tr[p].l==tr[p].r) return tr[p].val;
	pushdown(p);
	int mid=tr[p].l+tr[p].r>>1;
	if(x<=mid) return q_xor(p<<1,x);
	return q_xor(p<<1|1,x); 
}
void add_path(int x,int y,int v){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		m_add(1,dfn[top[x]],dfn[x],v);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	m_add(1,dfn[x]+1,dfn[y],v);
}
void xor_path(int x,int y,int v){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		m_xor(1,dfn[top[x]],dfn[x],v);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	m_xor(1,dfn[x]+1,dfn[y],v);
}
int main(){
	n=read();
	for(int i=1;i<n;i++){
		int a=read(),b=read();
		addedge(a,b),addedge(b,a);
	}
	for(int i=1;i<n;i++)
		red_e[i].first=read(),red_e[i].second=read();
	dfs1(1);
	top[1]=1; dfs2(1);
	build(1,1,n);
	for(int i=1;i<n;i++){
		int x=red_e[i].first,y=red_e[i].second;
		add_path(x,y,1);
		xor_path(x,y,i);
	}
	m_add(1,1,1,INF);
	for(int i=1;i<n;i++){
		int v=tr[1].dat,p=tr[1].loc;
		if(v!=1) return puts("NO"),0;
		int id=q_xor(1,p);
		int x=red_e[id].first,y=red_e[id].second;
		add_path(x,y,-1);
		xor_path(x,y,id);
		m_add(1,p,p,INF); 
	}
	puts("YES");
	return 0;
}

AGC001F

https://atcoder.jp/contests/agc001/tasks/agc001_f

好题

https://www.luogu.com.cn/blog/PinkRabbit/solution-at1984

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cctype>
using namespace std;
inline int read(){
	int x=0; char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x;
}
const int maxn=5e5+10;
int n,k,p[maxn],ans[maxn];
struct SegTree{ int l,r,pos;}tr[maxn*4];
inline void pushup(int x){
	tr[x].pos=p[tr[x<<1].pos]>p[tr[x<<1|1].pos]?tr[x<<1].pos:tr[x<<1|1].pos;
}
void build(int o,int l,int r){
	tr[o].l=l,tr[o].r=r;
	if(l==r) return tr[o].pos=l,void();
	int mid=l+r>>1;
	build(o<<1,l,mid),build(o<<1|1,mid+1,r);
	pushup(o);
}
void modify(int o,int x){
	if(tr[o].l==tr[o].r) return tr[o].pos=0,void();
	int mid=tr[o].l+tr[o].r>>1;
	if(x<=mid) modify(o<<1,x);
	else modify(o<<1|1,x);
	pushup(o);
}
int query(int o,int l,int r){
	if(l<=tr[o].l&&r>=tr[o].r) return tr[o].pos;
	int mid=tr[o].l+tr[o].r>>1,tmp=-1;
	if(l<=mid) tmp=query(o<<1,l,r);
	if(r>mid){
		if(~tmp){
			int res=query(o<<1|1,l,r);
			return p[res]>p[tmp]?res:tmp;
		}
		tmp=query(o<<1|1,l,r);
	}
	return tmp;
}
priority_queue<int> Q;
bool inq[maxn];
inline void ins(int x){
	if(query(1,max(x-k+1,1),min(x+k-1,n))==x)
		Q.push(x),inq[x]=true;
}
int main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++) p[i]=read();
	build(1,1,n);
	for(int i=1;i<=n;i++) ins(i);
	for(int i=n;i;i--){
		int t=Q.top(); Q.pop();
		ans[t]=i;
		modify(1,t);
		int pos=0;
		if(t>1) pos=query(1,max(t-k+1,1),t-1);
		if(pos&&!inq[pos]) ins(pos);
		if(t<n) pos=query(1,t+1,min(t+k-1,n));
		if(pos&&!inq[pos]) ins(pos);
	}
	for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
	return 0;
}

AGC001E

https://atcoder.jp/contests/agc001/tasks/agc001_e

\(\sum\limits_{1\leq i<j\leq n}\dbinom{a_i+b_i+a_j+b_j}{a_i+a_j}\)

推柿子?不可能的

考虑组合意义,即点 \((-a_i,-b_i)\) 走到 \((a_j,b_j)\) 的方案数。

想了好长时间不会,看完题解才注意到 \(A_i,B_i\leq2000\) 所以直接按过河卒方式 DP 出所有三象限点到一象限每个点的方案数总和,答案就很好统计了。复杂度 \(O((\max{A_i}+\max{B_i})^2+N)\)

也是好题

#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
inline int read(){
	int x=0; char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x;
}
typedef long long ll;
const int maxn=2e5+10;
const int N=2e3+10;
const int mod=1000000007;
int n,a[maxn],b[maxn],fac[N*4],inv[N*4];
int f[N*2][N*2];
inline int power(int a,int b){
	int ans=1;
	for(;b;b>>=1){
		if(b&1) ans=(ll)ans*a%mod;
		a=(ll)a*a%mod;
	}
	return ans;
}
void Init(){
	fac[0]=1;
	for(int i=1;i<N*4;i++) fac[i]=(ll)fac[i-1]*i%mod;
	inv[N*4-1]=power(fac[N*4-1],mod-2);
	for(int i=N*4-2;i;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod;
}
inline int C(int n,int m){
	return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),b[i]=read(),f[N-a[i]][N-b[i]]++;
	for(int i=-N+1;i<N;i++)
		for(int j=-N+1;j<N;j++)
			(f[N+i][N+j]+=f[N+i-1][N+j]+f[N+i][N+j-1])%=mod;
	Init();
	int ans=0;
	for(int i=1;i<=n;i++)
		ans=(ans+(ll)inv[2]*(f[N+a[i]][N+b[i]]-C(2*a[i]+2*b[i],2*a[i])))%mod;
	printf("%d\n",(ans+mod)%mod);
	return 0;
}
posted @ 2021-04-18 20:47  iMya_nlgau  阅读(76)  评论(0)    收藏  举报