[The 2019 Asia Yinchuan First Round Online Programming]简要题解

前言

吃瓜吃瓜
顺便我没有做过原题来试一试能打多少
由于开学机房只剩我一个人只能单挑了_ (:з」∠) _
有的题没时间写就口胡了

题目链接

Maximum Element In A Stack

Solution

模拟

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef unsigned int u32;
typedef long long ll;

const int N=5e6+5;

int n,p,q,m,ty,ct,top;

u32 SA,SB,SC,mx[N];

u32 rng61() { 
	SA^=SA<<16;SA^=SA>>5;SA^=SA<<1;
	u32 t=SA;
	SA=SB;SB=SC;SC^=t^SA;
	return SC;
}

void POP() {if (top) top--;}

void PUSH(u32 x) {
	++top;
	mx[top]=max(mx[top-1],x);
}

void gen() {
	scanf("%d%d%d%d%u%u%u",&n,&p,&q,&m,&SA,&SB,&SC);
	top=0;++ct;
	ll ans=0;
	fo(i,1,n) {
		if (rng61()%(p+q)<p) PUSH(rng61()%m+1);
		else POP();
		ans^=(ll)i*mx[top];
	}
	printf("Case #%d: %lld\n",ct,ans);
}

int main() {
	for(scanf("%d",&ty);ty;ty--) gen();
	return 0;
}

Rolling The Polygon

Solution

模拟

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef double db;

const int N=55;

struct P{
	db x,y;
	P(db _x=0,db _y=0) {x=_x;y=_y;}
	friend P operator + (P a,P b) {return P(a.x+b.x,a.y+b.y);}
	friend P operator - (P a,P b) {return P(a.x-b.x,a.y-b.y);}
	friend P operator * (P a,db b) {return P(a.x*b,a.y*b);}
	friend db operator * (P a,P b) {return a.x*b.x+a.y*b.y;}
	friend db operator ^ (P a,P b) {return a.x*b.y-a.y*b.x;}
}p[N],q;

db len(P a) {return sqrt(a*a);}

P rotate(P a,db b) {return P(a.x*cos(b)+a.y*sin(b),a.y*cos(b)-a.x*sin(b));}

db angle(P a) {return atan2(a.y,a.x);}

int n,ct;

void solve() {
	++ct;
	scanf("%d",&n);
	fo(i,1,n) scanf("%lf%lf",&p[i].x,&p[i].y);scanf("%lf%lf",&q.x,&q.y);
	fo(i,2,n) p[i].x-=p[1].x,p[i].y-=p[1].y;q.x-=p[1].x;q.y-=p[1].y;p[1].x=p[1].y=0;
	db age=angle(p[2]);
	fo(i,2,n) p[i]=rotate(p[i],age);q=rotate(q,age);
	p[n+1]=p[1];p[n+2]=p[2];
	db ans=0;
	fo(i,2,n+1) {
		P dir=p[i+1]-p[i];
		db age=angle(dir);
		fo(j,1,n+2) if (j!=i) p[j]=p[i]+rotate(p[j]-p[i],age);
		q=p[i]+rotate(q-p[i],age);
		ans+=fabs(age)*len(q-p[i]);
	}
	printf("Case #%d: %.3lf\n",ct,ans);
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

Caesar Cipher

Solution

模拟

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

const int N=55;

int n,m,ct;
char s[N],t[N],st[N];

void solve() {
	ct++;
	scanf("%d%d",&n,&m);
	scanf("%s",s+1);scanf("%s",t+1);scanf("%s",st+1);
	int shift=t[1]-s[1];if (shift<0) shift+=26;
	printf("Case #%d: ",ct);
	fo(i,1,m) putchar(st[i]-shift<'A'?st[i]-shift+26:st[i]-shift);
	puts("");
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

Take Your Seat

Solution

设F[n]表示n个人,最后一个人无法坐到正确位置的答案,则F[n]=i=1n1F[i]nF[n]={\sum_{i=1}^{n-1}F[i]\over n}
容易发现F[1]=1,F[2]=F[3]=F[4]=…=F[n]=0.5
第二问就是1i=2mF[i]m1-{\sum_{i=2}^{m}F[i]\over m},容易发现是m+12mm+1\over 2m

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef double db;

const int N=55;

int n,m,ct;

void solve() {
	ct++;
	scanf("%d%d",&n,&m);
	printf("Case #%d: %.6lf %.6lf\n",ct,n>1?0.5:1,(m+1)*1.0/(2*m));
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

2-3-4 Tree

Solution

英语阅读题(确信
对着题意模拟
懒得写了

Moving On

Solution

floyd求的就是只经过某些点的最短路
把所有点按权值排序离线

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

const int N=205,M=2e4+5;

int n,q,ct,w[N],d[N][N],ord[N],an[M];

struct Que{int u,v,w,id;}ask[M];

bool cmp1(int x,int y) {return w[x]<w[y];}
bool cmp2(Que x,Que y) {return x.w<y.w;}

void solve() {
	++ct;
	scanf("%d%d",&n,&q);
	fo(i,1,n) scanf("%d",&w[i]);
	fo(i,1,n) fo(j,1,n) scanf("%d",&d[i][j]);
	fo(i,1,n) ord[i]=i;
	sort(ord+1,ord+n+1,cmp1);
	fo(i,1,q) {
		scanf("%d%d%d",&ask[i].u,&ask[i].v,&ask[i].w);
		ask[i].id=i;
	}
	sort(ask+1,ask+q+1,cmp2);
	int j=0;
	for(int l=1,r=0;l<=n;l=r+1) {
		while (r<n&&w[ord[r+1]]==w[ord[l]]) r++;
		while (j<q&&ask[j+1].w<w[ord[l]]) {
			j++;
			an[ask[j].id]=d[ask[j].u][ask[j].v];
		}
		fo(k,l,r) {
			int x=ord[k];
			fo(i,1,n) fo(j,1,n) if (i!=x&&j!=x&&i!=j) d[i][j]=min(d[i][j],d[i][x]+d[x][j]);
		}
	}
	while (j<q) {
		j++;
		an[ask[j].id]=d[ask[j].u][ask[j].v];
	}
	printf("Case #%d:\n",ct);
	fo(i,1,q) printf("%d\n",an[i]);
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

Factories

Solution

设F[i][k]表示i子树内选了k个叶子的方案数
直接背包可以证明是O(nk)的,注意转移顺序

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;

typedef long long ll;

const int N=1e5+5;
const ll inf=1e18;

int t[N<<1],nxt[N<<1],v[N<<1],lst[N],l;
void add(int x,int y,int z) {t[++l]=y;v[l]=z;nxt[l]=lst[x];lst[x]=l;}

int n,K,ct,deg[N],sz[N];
ll f[N][105],g[105];

void Dp(int x,int y) {
	rep(i,x) if (t[i]!=y) Dp(t[i],x);
	fo(i,0,K) f[x][i]=inf;f[x][0]=sz[x]=0;
	if (deg[x]==1) f[x][1]=0,sz[x]=1;
	rep(i,x)
		if (t[i]!=y) {
			fo(j,0,min(K,sz[x]+sz[t[i]])) g[j]=inf;
			fo(j,0,min(K,sz[x]))
				fo(k,0,min(sz[t[i]],K-j))
					g[j+k]=min(g[j+k],f[x][j]+f[t[i]][k]+v[i]*k*(K-k));
			fo(j,0,min(K,sz[x]+sz[t[i]])) f[x][j]=g[j];
			sz[x]+=sz[t[i]];
		}
}

void solve() {
	scanf("%d%d",&n,&K);++ct;
	fo(i,1,n) lst[i]=deg[i]=0;l=0;
	fo(i,1,n-1) {
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);add(y,x,z);
		deg[x]++;deg[y]++;
	}
	printf("Case #%d: ",ct);
	if (n==2) {
		printf("%d\n",K==1?0:v[1]);
		return;
	}
	fo(i,1,n) 
		if (deg[i]>1) {
			Dp(i,0);
			printf("%lld\n",f[i][K]);
			return;
		}
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

Fight Against Monsters

Solution

这不是一道做烂了的题改一改-_-||
血量没用可以换成攻击次数
按ATK/Times大到小排序最优
证明考虑相邻两个交换更优的条件

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;

const int N=1e5+5;

int n,ct;

struct Node{int t,a;}a[N];

bool cmp(Node a,Node b) {return (ll)a.t*b.a<(ll)b.t*a.a;}

void solve() {
	scanf("%d",&n);++ct;
	fo(i,1,n) {
		scanf("%d%d",&a[i].t,&a[i].a);
		int m=sqrt(2*a[i].t);
		while (m*(m-1)>=2*a[i].t) m--;
		while (m*(m+1)<2*a[i].t) m++;
		a[i].t=m;
	}
	sort(a+1,a+n+1,cmp);
	ll sum=0,ans=0;
	fd(i,n,1) {
		sum+=a[i].a;
		ans+=sum*a[i].t;
	}
	printf("Case #%d: %lld\n",ct,ans);
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

Bubble Sort

Solution


感觉是最难的题(

Nested Triangles

Solution

分成上下两部分考虑
每个点可以看做直线QA和PA的交点
把所有的QA排序,所有的PA排序,可以包含A的点是一个二维矩形
用二维数据结构维护Dp
懒得写

Vertex Covers

Solution

折半

Code

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
using namespace std;

typedef long long ll;

const int N=40,S=(1<<18)+5;

vector< pair<int,int> > A,B,C;

typedef vector< pair<int,int> > :: iterator it;

int n,m,Mo,ct,w[N],f[S];

void solve() {
	scanf("%d%d%d",&n,&m,&Mo);ct++;
	A.clear();B.clear();C.clear();
	fo(i,1,n) scanf("%d",&w[i]);
	int L=n>>1;
	fo(s,0,(1<<L)-1) f[s]=0;
	fo(i,1,m) {
		int x,y;
		scanf("%d%d",&x,&y);
		if (x>y) swap(x,y);
		if (y<=L) A.pb(mp(x,y));
		else if (x>L) B.pb(mp(x-L,y-L));
		else C.pb(mp(x,y-L));
	}
	fo(s,0,(1<<L)-1) {
		bool ok=1;
		for(it i=A.begin();i!=A.end();i++) {
			int x=(*i).first,y=(*i).second;
			if (!(s>>(x-1)&1)&&!(s>>(y-1)&1)) {ok=0;break;}
		}
		if (!ok) continue;
		int ret=1;
		fo(i,1,L) if (s>>(i-1)&1) ret=(ll)ret*w[i]%Mo;
		f[s]=ret;
	}
	fo(i,0,L-1) fd(s,(1<<L)-1,0) if (s>>i&1) (f[s-(1<<i)]+=f[s])%=Mo;
	int ans=0;
	fo(s,0,(1<<n-L)-1) {
		bool ok=1;
		for(it i=B.begin();i!=B.end();i++) {
			int x=(*i).first,y=(*i).second;
			if (!(s>>(x-1)&1)&&!(s>>(y-1)&1)) {ok=0;break;}
		}
		if (!ok) continue;
		int ret=1;
		fo(i,1,n-L) if (s>>(i-1)&1) ret=(ll)ret*w[i+L]%Mo;
		int t=0;
		for(it i=C.begin();i!=C.end();i++) {
			int x=(*i).first,y=(*i).second;
			if (!(s>>(y-1)&1)) t|=1<<x-1;
		}
		(ans+=(ll)ret*f[t]%Mo)%=Mo;
	}
	printf("Case #%d: %d\n",ct,ans);
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

Continuous Intervals

Solution

合法条件等价于mx-mn+1=cnt
枚举右端点,考虑所有左端点的mx-mn-cnt
这个值显然恒>=-1
线段树区间加维护区间min及个数即可

Code

#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;

const int N=1e5+5;

struct Seg{int mn,cnt;}tr[N<<2];

int n,ct,tag[N<<2],a[N],sta_1[N],sta_2[N],top_1,top_2;
map<int,int> h;

Seg operator + (Seg x,Seg y) {
	Seg z;
	z.mn=min(x.mn,y.mn);z.cnt=0;
	z.cnt+=(x.mn==z.mn)?x.cnt:0;
	z.cnt+=(y.mn==z.mn)?y.cnt:0;
	return z;
}

void build(int v,int l,int r) {
	tr[v].mn=0;tr[v].cnt=r-l+1;tag[v]=0;
	if (l==r) return;
	int mid=l+r>>1;
	build(v<<1,l,mid);
	build(v<<1|1,mid+1,r);
}

void add(int v,int z) {tr[v].mn+=z;tag[v]+=z;}

void down(int v) {
	if (tag[v]) {
		add(v<<1,tag[v]);
		add(v<<1|1,tag[v]);
		tag[v]=0;
	}
}

void modify(int v,int l,int r,int x,int y,int z) {
	if (x<=l&&r<=y) {add(v,z);return;}
	int mid=l+r>>1;down(v);
	if (x<=mid) modify(v<<1,l,mid,x,y,z);
	if (y>mid) modify(v<<1|1,mid+1,r,x,y,z);
	tr[v]=tr[v<<1]+tr[v<<1|1];
}

void solve() {
	scanf("%d",&n);h.clear();ct++;
	fo(i,1,n) scanf("%d",&a[i]);
	build(1,1,n);
	ll ans=0;top_1=top_2=0;
	fo(i,1,n) {
		while (top_1&&a[sta_1[top_1]]<=a[i]) {
			modify(1,1,n,sta_1[top_1-1]+1,sta_1[top_1],a[i]-a[sta_1[top_1]]);
			top_1--;
		}
		sta_1[++top_1]=i;
		while (top_2&&a[sta_2[top_2]]>=a[i]) {
			modify(1,1,n,sta_2[top_2-1]+1,sta_2[top_2],a[sta_2[top_2]]-a[i]);
			top_2--;
		}
		sta_2[++top_2]=i;
		int la=h[a[i]]+1;h[a[i]]=i;
		modify(1,1,n,la,i,-1);
		if (tr[1].mn==-1) ans+=tr[1].cnt;
	}
	printf("Case #%d: %lld\n",ct,ans);
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

Acyclic Orientation

Solution

设F[n][m]表示答案
显然有F[n][m]=i=1nF[ni][m](ni)(1)i+1F[n][m]=\sum_{i=1}^{n}F[n-i][m]*\binom{n}{i}*(-1)^{i+1}
F[n][m]=i=1mF[n][mi](mi)(1)i+1F[n][m]=\sum_{i=1}^{m}F[n][m-i]*\binom{m}{i}*(-1)^{i+1}
考虑组合意义,从(0,0)走到(n,m),每次往上或右走k步,会有1k!(1)k+1{1\over k!}(-1)^{k+1}的贡献,求所有走法的贡献和
两维分开,枚举走的步数最后乘个组合数,可以发现,走m步到n的方案数为[xn](ex+1)m[x^n](-e^{-x}+1)^m
展开生成函数就是i=0m(mi)(i)n(1)i\sum_{i=0}^{m}\binom{m}{i}(-i)^n(-1)^i
这个可以卷积求出
后面部分也是一个卷积
要MTT差评

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;
typedef long double db;

const int N=(1<<18)+5,M=32767;

int n,m,ct,rev[N],fac[N],inv[N],f[N],g[N],a[N],b[N],c[N],Mo;

int pwr(int x,int y) {
	int z=1;
	for(;y;y>>=1,x=(ll)x*x%Mo)
		if (y&1) z=(ll)z*x%Mo;
	return z;
}

const db pi=acos(-1);

struct Com{
	db r,i;
	Com (db _r=0,db _i=0) {r=_r;i=_i;}
	friend Com operator + (Com x,Com y) {return Com(x.r+y.r,x.i+y.i);}
	friend Com operator - (Com x,Com y) {return Com(x.r-y.r,x.i-y.i);}
	friend Com operator * (Com x,Com y) {return Com(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);}
}t1[N],t2[N],t3[N],t4[N];

Com conj(Com a) {return Com(a.r,-a.i);}

void DFT(Com *a,int len,int flag) {
	fo(i,0,len-1) if (i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1;i<len;i<<=1) {
		Com wn=Com(cos(pi/i),flag*sin(pi/i));
		for(int j=0;j<len;j+=i<<1) {
			Com w=Com(1,0);
			for(int k=0;k<i;++k,w=w*wn) {
				Com u=a[j+k],v=w*a[j+k+i];
				a[j+k]=u+v;a[j+k+i]=u-v;
			}
		}
	}
	if (flag==-1) fo(i,0,len-1) a[i].r/=len,a[i].i/=len;
}

void mult(int *a,int *b,int *c,int n,int m,int l) {
	int lg,len;
	for(lg=0,len=1;len<=n+m;len<<=1) lg++;
	fo(i,0,len-1) rev[i]=rev[i>>1]>>1|((i&1)<<(lg-1));
	fo(i,0,len-1) t1[i]=t2[i]=t3[i]=t4[i]=Com(0,0);
	fo(i,0,n) t1[i]=Com(a[i]&M,a[i]>>15);
	fo(i,0,m) t2[i]=Com(b[i]&M,b[i]>>15);
	DFT(t1,len,1);DFT(t2,len,1);
	fo(i,0,len-1) {
		int j=(len-i)&(len-1);
		Com da=(t1[i]+conj(t1[j]))*Com(0.5,0);// a%r
		Com db=(t1[i]-conj(t1[j]))*Com(0,-0.5);// a/r
		Com dc=(t2[i]+conj(t2[j]))*Com(0.5,0);// b%r
		Com dd=(t2[i]-conj(t2[j]))*Com(0,-0.5);// b/r
		t3[i]=da*dc+da*dd*Com(0,1);
		t4[i]=db*dc+db*dd*Com(0,1);
	}
	DFT(t3,len,-1);DFT(t4,len,-1);
	fo(i,0,l) {
		int ka=(ll)(t3[i].r+0.5)%Mo,kb=(ll)(t3[i].i+0.5)%Mo;
		int kc=(ll)(t4[i].r+0.5)%Mo,kd=(ll)(t4[i].i+0.5)%Mo;
		c[i]=((ll)ka+((ll)(kb+kc)<<15)+((ll)kd<<30))%Mo;
	}
}

void solve() {
	scanf("%d%d%d",&n,&m,&Mo);ct++;
	fo(i,0,n+m) f[i]=g[i]=a[i]=b[i]=c[i]=0;
	fac[0]=1;fo(i,1,m+n) fac[i]=(ll)fac[i-1]*i%Mo;
	inv[m+n]=pwr(fac[m+n],Mo-2);fd(i,m+n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%Mo;
	fo(i,0,max(n,m)) g[i]=inv[i];
	fo(i,0,n) f[i]=(ll)inv[i]*pwr(-i,n)%Mo*(i&1?1:-1),(f[i]+=Mo)%=Mo;
	mult(f,g,a,n,n,n);
	fo(i,0,m) f[i]=(ll)inv[i]*pwr(-i,m)%Mo*(i&1?1:-1),(f[i]+=Mo)%=Mo;
	mult(f,g,b,m,m,m);
	a[0]=b[0]=0;mult(a,b,c,n,m,n+m);
	int ans=0;
	fo(i,1,n+m) (ans+=(ll)c[i]*fac[i]%Mo)%=Mo;
	printf("Case #%d: %d\n",ct,(ans+Mo)%Mo);
}

int main() {
	int ty;
	for(scanf("%d",&ty);ty;ty--) solve();
	return 0;
}

后记

就算不出原题,感觉这套题的难度也有点简单了
怎么说,希望下次组委会能够先调查好主办方吧
愿ICPC越办越好(

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值