2016-2017 ACM-ICPC Asia-Bangkok Regional Contest

A. WSI Extreme

将人按洗澡时间从大到小排序,那么$ans=\sum_{i=1}^{n}a_i\times\lfloor\frac{i+W-1}{W}\rfloor$。

当$W$比较大时,暴力枚举每一段,然后求和即可,权值线段树维护。

当$W$比较小时,线段树上按排名模$W$的值维护$W$个量即可。

时间复杂度$O(q\sqrt{W}\log n)$。

 

B. Average

枚举平均数然后容斥+隔板法计算方案数即可。

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

typedef long long LL ;
const int mod=1e9+7,Maxn=60*202;
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int rev[Maxn],fac[Maxn];
int C(int x,int y){
	if(x<y||y<0)return 0;
	return 1LL*fac[x]*rev[y]%mod*rev[x-y]%mod;
}
void solve(){
	fac[0]=fac[1]=rev[0]=rev[1]=1;
	for(int i=2;i<Maxn;i++)fac[i]=1LL*fac[i-1]*i%mod;
	for(int i=2;i<Maxn;i++)rev[i]=1LL*(mod-mod/i)*rev[mod%i]%mod;
	for(int i=2;i<Maxn;i++)rev[i]=1LL*rev[i-1]*rev[i]%mod;
}
int check(int sum,int n,int m){
	int ret=0;
	for(int i=0;i<=n;i++){
		int tmp=1LL*C(n,i)*C(sum+n-i*(m+1)-1,n-1)%mod;
		//if(i==1)printf("t1=%d t2=%d\n",C(n,i),C(sum+n-i*(m+1)-1,n-1));
		if(i&1)tmp=(mod-tmp)%mod;
		up(ret,tmp);
		//printf("ret=%d\n",ret);
	}
	return ret;
}
int main () {
	solve();
	
		//printf("t2=%d\n",check(2,2,1));
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF){
		if(!n&&!m)break;
		int ans=0;
		for(int i=0;i<=m;i++){
			up(ans,1LL*check((n-1)*i,n-1,m)%mod);
		}
		//printf("t1=%d\n",check(0,2,1));
		
		//printf("t2=%d\n",check(2,2,1));
		ans=1LL*ans*n%mod;
		printf("%d\n",ans);
	}
	return 0 ;
}

  

C. Big Bang

$ans=\sum_{i=1}^n\mu(i)\lfloor\frac{n}{i}\rfloor^4$。

 

D. Find C

首先通过exgcd求出一组解,然后往两侧延伸$K$个即可。

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

typedef long long LL ;
const LL LIM=100000000000000LL;
typedef pair<LL,LL>pi;
LL sx,sy,ex,ey;
int K;
LL gcd(LL x,LL y){
	if(!x)return y;
	if(!y)return x;
	return __gcd(x,y);
}
inline LL exgcd(LL a,LL b,LL &x,LL &y){
	if(!b)return x=1,y=0,a;
	LL d=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}
int ok(LL x,LL y){
	if(abs(x)>LIM||abs(y)>LIM)return 0;
	return 1;
}
bool check(LL x,LL y){
	if(x==sx&&y==sy)return 0;
	if(x==ex&&y==ey)return 0;
	if(__gcd(abs(x-sx),abs(y-sy))>1)return 0;
	if(__gcd(abs(x-ex),abs(y-ey))>1)return 0;
	return 1;
}
/*
void getst(LL &x,LL &y,LL stepx,LL stepy){
	if(!stepx){
		if(stepy<0){
			y%=-stepy;
		}
		else {
			y%=stepy;
		}
	}
	else
	if(!stepy){
		x%=stepx;
	}
	else{
		if(stepy>0){
			LL 
		}
	}
}
*/
set<pi>res;
void solve(LL S){
	LL A=abs(sy-ey),B=abs(sx-ex);
	LL gc=gcd(A,B);
	LL tA=(sy-ey)/gc,tB=(ex-sx)/gc;
	LL ftA=abs(tA),ftB=abs(tB);
	LL C=S-(sx*ey-ex*sy);
	C/=gc;
	LL x,y;
	exgcd(ftA,ftB,x,y);
	if(ftB){
		x=x*(C%ftB)%ftB;
		y=(C-x*ftA)/ftB;
	}
	else{
		y=y*(C%ftA)%ftA;
		x=(C-y*ftB)/ftA;
	}
	if(tA<0)x=-x;
	if(tB<0)y=-y;
	LL stepx=(ex-sx)/gc,stepy=(ey-sy)/gc;
	//if(!ok(x,y))while(1);
	for(LL curx=x+stepx,cury=y+stepy;res.size()<K&&ok(curx,cury);curx+=stepx,cury+=stepy){
		if(check(curx,cury)){
			//printf("curx=%lld cury=%lld\n",curx,cury);
			res.insert(pi(curx,cury));
		}
	}
	
	for(LL curx=x,cury=y;res.size()<K&&ok(curx,cury);curx-=stepx,cury-=stepy){
		if(check(curx,cury)){
			res.insert(pi(curx,cury));
		}
	}
}
int main () {
	int _;scanf("%d",&_);
	while(_--){
		scanf("%lld%lld%lld%lld%d",&sx,&sy,&ex,&ey,&K);
		if(ex<sx){swap(sx,ex);swap(sy,ey);}
		res.clear();
		LL S=__gcd(abs(sx-ex),abs(sy-ey));
		solve(S);
		solve(-S);
		if(res.size()<K)while(1);
		for(set<pi>::iterator it=res.begin();it!=res.end();it++){
			printf("%lld %lld\n",it->first,it->second);
		}
	}
	return 0 ;
}

  

E. ACM Tax

可持久化线段树维护即可。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=50010,M=N*20;
int T,C;
int n,m,i,x,y,z,g[N],v[N<<1],w[N<<1],nxt[N<<1],ed;
int f[N],d[N],size[N],son[N],top[N];
int tot,rt[N],l[M],r[M],val[M];
inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
int ins(int x,int a,int b,int c){
  int y=++tot;
  val[y]=val[x]+1;
  if(a==b)return y;
  int mid=(a+b)>>1;
  if(c<=mid){
    l[y]=ins(l[x],a,mid,c);
    r[y]=r[x];
  }else{
    l[y]=l[x];
    r[y]=ins(r[x],mid+1,b,c);
  }
  return y;
}
void dfs(int x){
  size[x]=1;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
    f[v[i]]=x,d[v[i]]=d[x]+1;
    rt[v[i]]=ins(rt[x],1,100000,w[i]);
    dfs(v[i]),size[x]+=size[v[i]];
    if(size[v[i]]>size[son[x]])son[x]=v[i];
  }
}
void dfs2(int x,int y){
  top[x]=y;
  if(son[x])dfs2(son[x],y);
  for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
}
inline int lca(int x,int y){
  for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y);
  return d[x]<d[y]?x:y;
}
inline int kth(int x,int y,int z,int k){
  int a=1,b=100000,mid,t;
  while(a<b){
    mid=(a+b)>>1;
    t=val[l[x]]+val[l[y]]-2*val[l[z]];
    if(k<=t){
      b=mid;
      x=l[x];
      y=l[y];
      z=l[z];
    }else{
      k-=t;
      a=mid+1;
      x=r[x];
      y=r[y];
      z=r[z];
    }
  }
  return a;
}
inline int query(int x,int y){
  int z=lca(x,y);
  int all=d[x]+d[y]-d[z]*2;
  if(all&1)return kth(rt[x],rt[y],rt[z],(all+1)/2)*2;
  return kth(rt[x],rt[y],rt[z],all/2)+kth(rt[x],rt[y],rt[z],all/2+1);
}
int main(){
  scanf("%d",&T);
  for(C=1;C<=T;C++){
    scanf("%d",&n);
    ed=0;
    for(i=1;i<=n;i++)g[i]=f[i]=d[i]=size[i]=son[i]=top[i]=rt[i]=0;
    for(i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
    dfs(1);
    dfs2(1,1);
    scanf("%d",&m);
    while(m--)scanf("%d%d",&x,&y),printf("%.1f\n",0.5*query(x,y));
    for(i=1;i<=tot;i++)l[i]=r[i]=val[i]=0;
    tot=0;
  }
}

  

F. Dictionary Game

建出Trie树,然后就是经典树上删边游戏。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100010*40;
int T,C,tot,son[N][26],sg[N],f[N];
int n,i,j;
inline int ins(){
  static char s[99];
  scanf("%s",s);
  int l=strlen(s);
  int x=0;
  for(int i=0;i<l;i++){
    int w=s[i]-'a';
    if(!son[x][w]){
      son[x][w]=++tot;
      f[tot]=x;
    }
    x=son[x][w];
  }
  return x;
}
inline void cal(int x){
  sg[x]=0;
  for(int i=0;i<26;i++)if(son[x][i])sg[x]^=sg[son[x][i]]+1;
}
void dfs(int x){
  for(int i=0;i<26;i++)if(son[x][i])dfs(son[x][i]);
  cal(x);
}
inline void up(int x){
  while(x){
    cal(x);
    x=f[x];
  }
  cal(0);
}
int main(){
  scanf("%d",&T);
  for(C=1;C<=T;C++){
    printf("Case %d:\n",C);
    scanf("%d",&n);
    while(n--)ins();
    dfs(0);
    scanf("%d",&n);
    while(n--){
      up(ins());
      puts(sg[0]?"1":"2");
    }
    for(i=0;i<=tot;i++)for(j=f[i]=sg[i]=0;j<26;j++)son[i][j]=0;
    tot=0;
  }
}

  

G. Binary Strings

矩阵快速幂优化DP转移。

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

typedef long long LL ;
const int mod=1e9+7;
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
struct Mat{
	int a[3][3];
	Mat operator *(const Mat&x)const{
		Mat ret;
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				int t=0;
				for(int k=0;k<3;k++){
					up(t,1LL*a[i][k]*x.a[k][j]%mod);
				}
				ret.a[i][j]=t;
			}
		}
		return ret;
	}
	void init(int t){
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				a[i][j]=i==j?t:0;
			}
		}
	}
}ori;
Mat powmod(Mat x,LL y){
	Mat ret;
	ret.init(1);
	while(y){
		if(y&1){ret=ret*x;}
		y>>=1;
		x=x*x;
	}
	return ret;
}
int go(LL x){
	Mat tmp=powmod(ori,x+1);
	return tmp.a[0][2];
}
void solve(){
	LL l,r,k;
	scanf("%lld%lld%lld",&l,&r,&k);
	ori.init(0);
	ori.a[0][0]=1;
	ori.a[0][1]=1;
	ori.a[1][0]=1;
	ori=powmod(ori,k);
	ori.a[0][2]=1;
	ori.a[1][2]=1;
	ori.a[2][2]=1;
	ori.a[2][0]=ori.a[2][1]=0;
	int ans=go(r/k);
	ans-=go((l-1)/k);
	if(ans<0)ans+=mod;
	printf("%d\n",ans);
}
int main () {
	int _,cas=1;scanf("%d",&_);
	while(_--){
		printf("Case %d: ",cas++);
		solve();
	}
	return 0 ;
}

  

H. Witcher Potion

$f[S][i][j][k]$表示已经使用过的药水集合为$S$,当前能量为$i$,毒性为$j$,上一次是否嗑药为$k$时最多能打多少只怪物,然后DP即可。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long ll;
const int inf=10000000;
int T,K,M,n,i,j,k,ans,t,e[11],p[11],f[1<<8][111][111][2],o;
inline void up(int&x,int y){if(x<y)x=y;}
int main(){
  scanf("%d",&T);
  while(T--){
    scanf("%d%d%d",&K,&M,&n);
    for(i=0;i<n;i++)scanf("%d",&e[i]);
    for(i=0;i<n;i++)scanf("%d",&p[i]);
    for(i=0;i<1<<n;i++)for(j=1;j<=100;j++)for(k=0;k<100;k++)f[i][j][k][0]=f[i][j][k][1]=-inf;
    up(f[0][100][0][0],0);
    for(i=0;i<1<<n;i++)for(j=100;j;j--)for(k=99;~k;k--)for(o=0;o<2;o++)if(f[i][j][k][o]>=0){
      //kill a monster
      if(j>K)up(f[i][j-K][max(0,k-M)][0],f[i][j][k][o]+1);
      //drink
      if(!o)for(t=0;t<n;t++)if(!(i>>t&1)){
        if(k+p[t]<100)up(f[i|(1<<t)][min(100,j+e[t])][k+p[t]][1],f[i][j][k][o]);
      }

    }
    ans=0;
    for(i=0;i<1<<n;i++)for(j=1;j<=100;j++)for(k=0;k<100;k++){
      up(ans,f[i][j][k][0]);
      up(ans,f[i][j][k][1]);
    }
    printf("%d\n",ans);
  }
}

  

I. Sky Tax

根据根与询问点的位置关系分类讨论即可。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int N=1000010,M=2000010;
int T,C,n,m,root,x,y,i,g[N],v[N<<1],nxt[N<<1],ed;
int st[N],en[N],size[N],dfn;
int son[N],top[N],d[N],f[N];
inline void add(int x,int y){
  v[++ed]=y;nxt[ed]=g[x];g[x]=ed;
}
void dfs(int x){
  size[x]=1;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
    f[v[i]]=x,d[v[i]]=d[x]+1;
    dfs(v[i]);
    size[x]+=size[v[i]];
    if(size[v[i]]>size[son[x]])son[x]=v[i];
  }
}
void dfs2(int x,int y){
  st[x]=++dfn;top[x]=y;
  if(son[x])dfs2(son[x],y);
  for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
  en[x]=dfn;
}
inline int lca2(int x,int y){
  int t;
  while(top[x]!=top[y])t=top[y],y=f[top[y]];
  return x==y?t:son[x];
}
inline int ask(int x){
  if(root==x)return n;
  if(st[x]>st[root]||en[x]<en[root])return size[x];
  return n-size[lca2(x,root)];
}
void check(int x){
  if(x<1||x>n)exit(0);
}
int main(){
  scanf("%d",&T);
  for(C=1;C<=T;C++){
    printf("Case #%d:\n",C);
    scanf("%d%d%d",&n,&m,&root);
    check(root);
    for(ed=dfn=0,i=1;i<=n;i++)g[i]=0;
    for(i=1;i<=n;i++)size[i]=son[i]=top[i]=f[i]=d[i]=0;
    for(i=1;i<n;i++){
      //x=i+1;y=rand()%i
      scanf("%d%d",&x,&y);
      check(x);
      check(y);
      add(x,y),add(y,x);
    }
    dfs(1);
    dfs2(1,1);
    while(m--){
      scanf("%d%d",&x,&y);
      check(y);
      if(x==0)root=y;
      else{
        printf("%d\n",ask(y));
      }
    }
  }
}

  

J. Printing Press

留坑。

 

K. Expected Number of Connected Components

留坑。

 

L. Coordinates

建树DFS即可确定出每个点的坐标。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010,M=2000010;
const ll inf=1LL<<60;
int n,m,i,f[N],g[N],v[M],wx[M],wy[M],nxt[M],ed;
ll minx,miny,basex[N],basey[N],xx[N],yy[N];
inline void add(int x,int y,int dx,int dy){
  v[++ed]=y;
  wx[ed]=dx;
  wy[ed]=dy;
  nxt[ed]=g[x];
  g[x]=ed;
}
void dfs(int x,int y,ll X,ll Y){
  if(f[x])return;
  f[x]=y;
  xx[x]=X;
  yy[x]=Y;
  minx=min(minx,X);
  miny=min(miny,Y);
  for(int i=g[x];i;i=nxt[i])dfs(v[i],y,X+wx[i],Y+wy[i]);
}
int main(){
  scanf("%d%d",&n,&m);
  while(m--){
    int x,y,dx,dy;
    scanf("%d%d%d%d",&x,&y,&dx,&dy);
    add(x,y,dx,dy);
    add(y,x,-dx,-dy);
  }
  for(i=1;i<=n;i++)if(!f[i]){
    minx=inf;
    miny=inf;
    dfs(i,i,0,0);
    basex[i]=-minx;
    basey[i]=-miny;
  }
  for(i=1;i<=n;i++)printf("%I64d %I64d\n",xx[i]+basex[f[i]],yy[i]+basey[f[i]]);
}

 


总结:

  • I题发现模板有误。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值