IOI2020集训队作业-9 (CF538G, CF674D, ARC101F)

A - CF538G Berserk Robot

Sol

将原题中的坐标 ( x , y ) (x,y) (x,y)变成 ( x + y , x − y ) (x+y,x-y) (x+y,xy),原题的行走一步改成 ( 1 , 1 ) , ( 1 , − 1 ) , ( − 1 , 1 ) , ( − 1 , − 1 ) (1,1),(1,-1),(-1,1),(-1,-1) (1,1),(1,1),(1,1),(1,1)。这样与原来的问题是等价的,并且横纵坐标变得独立了。

单独对每一维考虑。设走完这 l l l步坐标的变化量为 d x dx dx

如果有两个 t i t_i ti l l l的余数相同,就可以解出 d x dx dx,然后进行构造。

否则将已知的点将 t i   m o d   l t_i \bmod l timodl排序,相邻的两个算一下它们对 d x dx dx的限制。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#define db long double
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=getchar();}
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
int curtest;
void FAIL() { printf("NO"); exit(0); }
ll Abs(ll x) { return x>0?x:-x; }
db eps=1e-20;
int dcmp(db x) { return x>-eps?(x>eps):-1; }
const int N=2e6+10;
vector<int> pos;
struct node { ll t,x,y; }v[N];
ll val[N],ti[N];
int vis[N];
int m,n;
db lb,rb;
vector<char> S;
void modify(int p,ll x,ll delt) {
	if(Abs((delt*m+p)%2)^Abs(x%2)) FAIL();
	if(delt==0) return;
	ll l=-p-x,r=p-x; db dt=-delt;
	if(dt>0) lb=max(lb,l/dt),rb=min(rb,r/dt);
	else lb=max(lb,r/dt),rb=min(rb,l/dt);
}
void sol(vector<int> &ans) {
	lb=-m,rb=m;
	for(int i=0;i+1<pos.size();++i) {
		if(i+2==pos.size()) modify(m-pos[i],-val[pos[i]],-ti[pos[i]]-1);
		else modify(pos[i+1]-pos[i],val[pos[i+1]]-val[pos[i]],ti[pos[i+1]]-ti[pos[i]]);
	}
	ll dx=floor(rb);
	if(Abs(dx%2)^(m%2)) dx--;
	if(dcmp(dx-lb)<0) FAIL();
	for(int i=0;i+1<pos.size();++i) {
		int l=pos[i],r=pos[i+1],len=r-l;
		ll x=(r==m?dx:val[r])-val[l]-(ti[r]-ti[l])*dx;
		int d=x>0?1:-1;
		x=Abs(x); len-=x;
		while(x) x--,ans.PB(d);
		if(len<0||(len&1)) FAIL();
		while(len) ans.PB(1),ans.PB(-1),len-=2;
	}
}
 
ll dx,dy;
void solve(int l,int r,ll x,ll y,ll t) {
	x-=t*dx,y-=t*dy;
	char xx=x>0?'R':'L';
	char yy=y>0?'U':'D';
	x=Abs(x),y=Abs(y);
	while(x--) S.PB(xx),r--;
	while(y--) S.PB(yy),r--;
	if(((r-l)&1)||r-l<0) FAIL();
	while(r-l) S.PB('U'),S.PB('D'),r-=2;
}
 
vector<int> ax,ay;
int main() {
	rd(n),rd(m);
	int flg=0;
	vis[0]=1,v[0].x=v[0].y=v[0].t=0;
	for(int i=1;i<=n;++i) {
		node A;
		rd(A.t),rd(A.x),rd(A.y);
		int p=A.t%m;
		A.t/=m;
		if(vis[p]) {
			if(A.t==v[p].t) {
				if(A.x!=v[p].x||A.y!=v[p].y) FAIL();
				continue;
			}
			ll dt=A.t-v[p].t;
			if(flg) {
				if(v[p].x+dt*dx==A.x&&v[p].y+dt*dy==A.y) continue;
				else FAIL();
			}
			dx=A.x-v[p].x,dy=A.y-v[p].y;
			if(dx%dt||dy%dt) FAIL();
			dx/=dt,dy/=dt;
			flg=1;
		}
		else v[p]=A,vis[p]=1;
	}
	
	if(flg) {
		int p1=0,p2;
		while(true) {
			p2=p1+1;
			while(!vis[p2]&&p2<m) p2++;
			if(p2>=m) break;
			solve(p1,p2,v[p2].x-v[p1].x,v[p2].y-v[p1].y,v[p2].t-v[p1].t);
			p1=p2;
		}
		solve(p1,m,dx-v[p1].x,dy-v[p1].y,-v[p1].t);
		for(int i=0;i<S.size();++i) putchar(S[i]);
	}
	else {
		for(int i=0;i<m;++i) if(vis[i]) ti[i]=v[i].t,pos.PB(i);
		pos.PB(m),ti[m]=0;
		for(int i=0;i<m;++i) if(vis[i]) val[i]=v[i].x+v[i].y;
		sol(ax);
		for(int i=0;i<m;++i) if(vis[i]) val[i]=v[i].x-v[i].y;
		sol(ay);
		for(int i=0;i<m;++i) {
			if(ax[i]==ay[i]) putchar(ax[i]>0?'R':'L');
			else putchar(ax[i]>0?'U':'D');
		}
	}
	
	return 0;
}

B - CF674D Bearish Fanpages

Sol

对每个点维护它的所有儿子对它的贡献的和,这样可以在 O ( 1 ) O(1) O(1)的时间完成修改,在 O ( 1 ) O(1) O(1)的时间完成对某个点价值的查询。

维护全局最大最小值:对每个点 x x x维护一个multiset,里面存所有儿子的(这个儿子的价值- x x x对儿子的贡献),然后将multiset中的最大的和最小的加上 x x x的贡献放入一个全局的multiset。这样修改操作jiang可以在 O ( log ⁡ n ) O(\log n) O(logn)的时间内完成。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=getchar();}
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=1e5+10;
int du[N],fa[N],n;
ll ans[N],ti[N];
multiset<ll> s[N],que;
int tim,vis[N];
inline ll cot(int x) { return ti[x]/(du[x]+1); }
inline ll get(int x) { return ans[x]+(ti[x]-cot(x)*du[x]); }
inline ll getans(int x) { return cot(fa[x])+get(x); }
void Ins(int x) { if(vis[x]==tim) return; vis[x]=tim; if(!s[x].empty()) que.insert(cot(x)+(*s[x].rbegin())),que.insert(cot(x)+(*s[x].begin())); }
void Del(int x) { if(vis[x]==tim) return; vis[x]=tim; if(!s[x].empty()) que.erase(que.find(cot(x)+(*s[x].rbegin()))),que.erase(que.find((cot(x)+(*s[x].begin())))); }
void link(int x,int f) {
	int ff=fa[f];
	tim++,Del(x),Del(f),Del(ff),Del(fa[ff]);
	if(ff) {
		if(fa[ff]) s[fa[ff]].erase(s[fa[ff]].find(get(ff)));
		s[ff].erase(s[ff].find(get(f)));
		ans[ff]-=cot(f);
	}
		
	fa[x]=f,du[x]++,du[f]++;
	ans[f]+=cot(x);
	s[f].insert(get(x));
	
	if(ff) {
		ans[ff]+=cot(f);
		s[ff].insert(get(f));
		if(fa[ff]) s[fa[ff]].insert(get(ff));
	}
	tim++,Ins(x),Ins(f),Ins(ff),Ins(fa[ff]);
}
void cut(int x) {
	int f=fa[x],ff=fa[f];
	tim++,Del(x),Del(f),Del(ff),Del(fa[ff]);
	if(ff) {
		if(fa[ff]) s[fa[ff]].erase(s[fa[ff]].find(get(ff)));
		s[ff].erase(s[ff].find(get(f)));
		ans[ff]-=cot(f);
	}
	
	s[f].erase(s[f].find(get(x)));
	ans[f]-=cot(x);
	du[x]--,du[fa[x]]--,fa[x]=0;
	
	
	if(ff) {
		ans[ff]+=cot(f);
		s[ff].insert(get(f));
		if(fa[ff]) s[fa[ff]].insert(get(ff));
	}
	tim++,Ins(x),Ins(f),Ins(ff),Ins(fa[ff]);
}	
int main() {
	int q; rd(n),rd(q);
	for(int i=1;i<=n;++i) rd(ti[i]);
	for(int f,i=1;i<=n;++i) rd(f),link(i,f);
	while(q--) {
		int ty,x,y; rd(ty);
		if(ty==1) {
			rd(x),rd(y);
			cut(x);
			link(x,y);
		}
		else if(ty==2) rd(x),printf("%lld\n",getans(x));
		else printf("%lld %lld\n",*que.begin(),*que.rbegin());
	}
	return 0;
}

C - ARC101F Robots and Exits

Sol

对于每个机器人,显然它最终对应的出口只可能是它左边的第一个或者右边的第一个。假设机器人 i i i对应的两个出口到机器人 i i i的坐标变化量分别为 l i , r i l_i,r_i li,ri,其中 l i < 0 , r i > 0 l_i < 0, r_i > 0 li<0,ri>0

考虑机器人们的坐标相对于最初的坐标的变化量关于时间变化的函数,则对于每个机器人来说,它会从哪个口出去,只与这个函数与 y = l i y=l_i y=li y = r i y=r_i y=ri的第一个交点哪一个横坐标更小有关。

观察发现可能的方案数就是将所有的机器人按照 l i l_i li从小到大排序( l i l_i li相同则 r i r_i ri大的放在前面)之后, r i r_i ri所能够形成的上升子序列的数量。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
	x=0; char c=getchar(); int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=getchar();}
	while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=1e5+10,mod=1e9+7;
struct node {
	int x,y;
	node(int x=0,int y=0): x(x),y(y) {}
	friend bool operator <(node A,node B) { return A.x==B.x?A.y>B.y:A.x>B.x; }
	friend bool operator ==(node A,node B) { return A.x==B.x&&A.y==B.y; }
}a[N];
int xi[N],yi[N],n,m,cnt;
int val[N],tot;

int c[N];
void add(int i,int t) { for(;i<=tot;i+=i&-i) c[i]=(c[i]+t)%mod; }
int query(int i) { int ans=0; for(;i;i-=i&-i) ans=(ans+c[i])%mod; return ans; }
int main() {
	rd(n),rd(m);
	for(int i=1;i<=n;++i) rd(yi[i]);
	for(int i=1;i<=m;++i) rd(xi[i]);
	for(int i=1;i<=n;++i) {
		if(yi[i]<=xi[1]||yi[i]>=xi[m]) continue;
		int r=lower_bound(xi+1,xi+m+1,yi[i])-xi;
		int l=r-1;
		a[++cnt]=node(xi[l]-yi[i],xi[r]-yi[i]);
	}
	sort(a+1,a+cnt+1);
	cnt=unique(a+1,a+cnt+1)-a-1;
	for(int i=1;i<=cnt;++i) val[++tot]=a[i].y;
	sort(val+1,val+tot+1);
	tot=unique(val+1,val+tot+1)-val-1;	
	for(int i=1;i<=cnt;++i) a[i].y=lower_bound(val+1,val+tot+1,a[i].y)-val;
	int ans=1;
	for(int i=1;i<=cnt;++i) {
		int t=(query(a[i].y-1)+1)%mod;
		ans=(ans+t)%mod;
		add(a[i].y,t);
	}
	printf("%d",ans);
	return 0;
}

[i].y;
sort(val+1,val+tot+1);
tot=unique(val+1,val+tot+1)-val-1;
for(int i=1;i<=cnt;++i) a[i].y=lower_bound(val+1,val+tot+1,a[i].y)-val;
int ans=1;
for(int i=1;i<=cnt;++i) {
int t=(query(a[i].y-1)+1)%mod;
ans=(ans+t)%mod;
add(a[i].y,t);
}
printf("%d",ans);
return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值