USACO FEB18,Platinum

slingshot

       题意:给定n个单向传送门(a,b,c)和m次询问(x,y),求至多用一次传送门的最短路径长度。

       即求min(|x-y|,min{|a-x|+|b-y|+c}),考虑分类讨论后将绝对值打开,用主席树维护四个方向上对应的pa+qb+c(p,q为-1或1)的最小值即可。

AC代码如下:

#include<bits/stdc++.h>
#define ll long long
#define inf 1000000000
#define infll 1000000000000000000ll
#define N 100009
#define M 10000009
#define dn(x,y) (x>(y)?x=(y):0)
using namespace std;

int n,m,trtot,cnt,hsh[N],rt1[N],rt2[N],ls[M],rs[M];
struct node{ int x,y,z; }a[N];
bool cmpx(node u,node v){ return u.x<v.x; }
struct point{ ll x,y; }T[M];
void calc(point &u,point v){
	dn(u.x,v.x); dn(u.y,v.y);	
}
void ins(int &k,int p,int l,int r,int x,ll u,ll v){
	k=++trtot; T[k]=T[p]; calc(T[k],(point){u,v});
	if (l==r) return;
	int mid=l+r>>1;
	if (x<=mid){ rs[k]=rs[p]; ins(ls[k],ls[p],l,mid,x,u,v); }
	else{ ls[k]=ls[p]; ins(rs[k],rs[p],mid+1,r,x,u,v); }
}
point qry(int k,int l,int r,int x,int y){
	if (!k) return (point){infll,infll};
	if (x<=l && r<=y) return T[k];
	int mid=l+r>>1; point ans=(point){infll,infll};
	if (x<=mid) calc(ans,qry(ls[k],l,mid,x,y));
	if (y>mid) calc(ans,qry(rs[k],mid+1,r,x,y));
	return ans;
}
int main(){
	freopen("slingshot.in","r",stdin); freopen("slingshot.out","w",stdout);
	scanf("%d%d",&n,&m);
	int i,k,x,y;
	for (i=1; i<=n; i++){
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
		hsh[i]=a[i].x;
	}
	sort(hsh+1,hsh+n+1);
	cnt=unique(hsh+1,hsh+n+1)-hsh-1; hsh[++cnt]=inf+1;
	sort(a+1,a+n+1,cmpx);
	T[0].x=T[0].y=infll;
	for (i=1; i<=n; i++){
		k=lower_bound(hsh+1,hsh+cnt+1,a[i].x)-hsh;
		if (!rt1[k]) rt1[k]=rt1[k-1];
		ins(rt1[k],rt1[k],0,inf,a[i].y,a[i].z-a[i].x-a[i].y,a[i].z-a[i].x+a[i].y);
	}
	for (i=n; i; i--){
		k=lower_bound(hsh+1,hsh+cnt+1,a[i].x)-hsh;
		if (!rt2[k]) rt2[k]=rt2[k+1];
		ins(rt2[k],rt2[k],0,inf,a[i].y,a[i].z+a[i].x-a[i].y,(ll)a[i].z+a[i].x+a[i].y);
	}
	ll ans;
	while (m--){
		scanf("%d%d",&x,&y);
		ans=abs(y-x); k=lower_bound(hsh+1,hsh+cnt+1,x)-hsh;
		dn(ans,qry(rt2[k],0,inf,0,y).x-x+y);
		dn(ans,qry(rt2[k],0,inf,y,inf).y-x-y);
		dn(ans,qry(rt1[k-1],0,inf,0,y).x+x+y);
		dn(ans,qry(rt1[k-1],0,inf,y,inf).y+x-y);
		printf("%lld\n",ans);
	}
	return 0;
}


newbarn

       题意:支持合并和加点,多次询问到某点距离最远的点的距离。

       显然距离最远的点一定是直径端点。考虑维护每个连通块的直径,合并的时候新的直径两端点一定是原来两条直径四个端点中的两个。

AC代码如下:

#include<bits/stdc++.h>
#define N 100009
using namespace std;

int n,m,tot,fst[N],pnt[N<<1],nxt[N<<1],p[N][2],dad[N];
int a[N],fa[N],d[N],sz[N],anc[N],son[N];
bool vis[N]; char op[N][3];
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
	int i,y; vis[x]=1; sz[x]=1;
	for (i=fst[x]; i; i=nxt[i]){
		y=pnt[i];
		if (y!=fa[x]){
			fa[y]=x; d[y]=d[x]+1;
			dfs(y); sz[x]+=sz[y];
			if (sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void dvd(int x,int tp){
	int i,y; anc[x]=tp;
	if (son[x]) dvd(son[x],tp);
	for (i=fst[x]; i; i=nxt[i]){
		y=pnt[i];
		if (y!=fa[x] && y!=son[x]) dvd(y,y);
	}
}
int lca(int x,int y){
	for (; anc[x]!=anc[y]; x=fa[anc[x]])
		if (d[anc[x]]<d[anc[y]]) swap(x,y);
	return d[x]<d[y]?x:y;
}
int dist(int x,int y){ return d[x]+d[y]-(d[lca(x,y)]<<1); }
int getdad(int x){ return x==dad[x]?x:dad[x]=getdad(dad[x]); }
void mrg(int x,int y){
	int u=p[x][0],v=p[x][1],mx=dist(u,v),i,j,tmp;
	for (i=0; i<2; i++)
		for (j=0; j<2; j++)
			if ((tmp=dist(p[x][i],p[y][j]))>mx){
				mx=tmp; u=p[x][i]; v=p[y][j];
			}
	dad[x]=y;
	if (dist(p[y][0],p[y][1])>mx) return;
	p[y][0]=u; p[y][1]=v;
}
int main(){
	freopen("newbarn.in","r",stdin); freopen("newbarn.out","w",stdout);
	scanf("%d",&m);
	int i;
	for (i=1; i<=m; i++){
		scanf("%s",op[i]); scanf("%d",&a[i]);
		if (op[i][0]=='B'){
			n++;
			if (a[i]!=-1){
				add(n,a[i]); add(a[i],n);
			}
		}
	}
	for (i=1; i<=n; i++) if (!vis[i]){
		d[i]=1; dfs(i); dvd(i,i);
	}
	for (i=1; i<=n; i++){
		dad[i]=i; p[i][0]=p[i][1]=i;	
	}
	n=0;
	for (i=1; i<=m; i++) if (op[i][0]=='B'){
		n++;
		if (a[i]==-1) continue;
		if (getdad(n)==getdad(a[i])) continue;
		mrg(getdad(n),getdad(a[i]));
	} else
		printf("%d\n",max(dist(a[i],p[getdad(a[i])][0]),dist(a[i],p[getdad(a[i])][1])));
	return 0;
}

gymnasts

       题意:问有多少长度为N且每一个数<=N的数组a,

                 满足数组b(b[i]=\sum_{j=0}^{n}[a[i-j]>j])和数组a相同。

       首先令a[i]=a[i]-1。假设a中最大的数为a[k]=x,y=gcd(x,n),那么可知a[k-y]=x,a[k-y+1]=x-1。进一步可以知道任意a[i]=a[i-y],且a[i]=x或x-1。枚举y然后简单统计即可。

AC代码如下:

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;

int cnt,ans=1; ll n,p[19];
int ksm(int x,ll y){
	int z=1; for (; y; y>>=1,x=(ll)x*x%mod) if (y&1) z=(ll)z*x%mod;
	return z;
}
ll gcd(ll x,ll y){ return y?gcd(y,x%y):x; }
int calc(ll x){
	return ksm(2,x)-1;
	ll i; int ans=0;
	for (i=1; i<=x; i++) ans=(ans+ksm(2,gcd(x,i)))%mod;
	return (ll)ans*ksm(x,mod-2)%mod-1;
}
void solve(ll x){
	if (x==n) return;
	int i; ll y=n/x;
	for (i=1; i<=cnt; i++) if (!(y%p[i])) y=y/p[i]*(p[i]-1);
	//cerr<<x<<' '<<calc(x)<<' '<<y<<endl;
	ans=(ans+y%mod*calc(x))%mod;
}
int main(){
	freopen("gymnasts.in","r",stdin); freopen("gymnasts.out","w",stdout);
	scanf("%lld",&n);
	int i; ll x=n;
	for (i=2; (ll)i*i<=n; i++) if (!(x%i)){
		p[++cnt]=i;
		for (; !(x%i); x/=i);	
	}
	if (x>1) p[++cnt]=x;
	for (i=1; (ll)i*i<=n; i++) if (!(n%i)){
		solve(i);
		if (i!=n/i) solve(n/i);	
	}
	printf("%d\n",ans);
	return 0;
}

by lych

2018.2.28

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值