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;
}

浙公网安备 33010602011771号