AtCoder Grand Contest 018题解

传送门

(A)

根据裴蜀定理显然要(k|gcd(a_1,...,a_n)),顺便注意不能造出大于(max(a_1,...,a_n))的数

int n,g,k,x,mx;
int main(){
	scanf("%d%d",&n,&k);
	fp(i,1,n)scanf("%d",&x),g=__gcd(g,x),cmax(mx,x);
	puts(k%g==0&&k<=mx?"POSSIBLE":"IMPOSSIBLE");
	return 0;
}

(B)

对每个人开一个指针,表示他是参加了哪个运动,然后每次把参加人数最多的运动强制不选,同时更新每个人的指针。一直做到指针越界为止,取最优值即可

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=305;
int mp[N][N],cnt[N],id[N],bg[N],vis[N],res,mx,n,m;
int main(){
	scanf("%d%d",&n,&m);
	fp(i,1,n)fp(j,1,m)scanf("%d",&mp[i][j]);
	fp(i,1,n)bg[i]=1,++cnt[mp[i][1]];
	res=2333;
	while(2333){
		mx=0;fp(i,1,m)cmax(mx,cnt[i]);
		cmin(res,mx);
		fp(i,1,m)if(cnt[i]==mx)vis[i]=1;
		fp(i,1,n)while(bg[i]<=m&&vis[mp[i][bg[i]]])++bg[i];
		fp(i,1,n)if(bg[i]>m)return printf("%d
",res),0;
		fp(i,1,m)cnt[i]=0;
		fp(i,1,n)++cnt[mp[i][bg[i]]];
	}
	return 0;
}

(C)

先考虑二维的情况,即总共有(n=x+y)个人,每个人有金币(a_i)或银币(b_i),那么先假设所有人都取银币,加上前(x)大的(a_i-b_i)就行了。如果总的个数大于(n),那么按(a_i-b_i)从大到小排序后,取金币的人必然全部在取银币的人的左边,即存在一个中间点(k),满足取金币的人在([1,k]),取银币的人在([k+1,n])

对于三维,我们假设所有人都选铜币,并令(a_i-=c_i,b_i-=c_i),那么此时转化为之前那种二维的情况了,用堆维护就可以了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll f[N],g[N],sum,res;int a[N],b[N],c[N],id[N],x,y,z,n,sz;
priority_queue<int,vector<int>,greater<int> >q;
inline bool cmp(const int &x,const int &y){return a[x]-b[x]>a[y]-b[y];}
inline void ins(R int x){sum+=x,q.push(x),++sz;}
inline void del(){sum-=q.top(),q.pop(),--sz;}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d%d",&x,&y,&z),n=x+y+z;
	fp(i,1,n){
		scanf("%d%d%d",&a[i],&b[i],&c[i]);
		a[i]-=c[i],b[i]-=c[i],id[i]=i;
	}
	sort(id+1,id+1+n,cmp);
	sum=sz=0;while(!q.empty())q.pop();
	fp(i,1,n){
		ins(a[id[i]]);
		while(sz>x)del();
		if(sz==x)f[i]=sum;
	}
	sum=sz=0;while(!q.empty())q.pop();
	fd(i,n,1){
		ins(b[id[i]]);
		while(sz>y)del();
		if(sz==y)g[i]=sum;
	}
	res=-1e18;
	fp(i,x,n-y)cmax(res,f[i]+g[i+1]);
	fp(i,1,n)res+=c[i];
	printf("%lld
",res);
	return 0;
}

(D)

我是个(zz)……

首先对于每条边,设它两边的(size)里较小的那个为(s),它的权值为(w),那么它的贡献上界是(s imes w),那么答案上界就是(sum s imes w)

然而上界不可能达到的,我们考虑该减去那些

假设重心是一条边,即两边的(size)大小都是({nover 2}),那么对于除了重心的这条边,我们都可以令它被经过次数达到上界,那么答案就是上界减去这条边的权值

假设重心是一个点,那么除了重心到某个子节点的一条边,我们也可以让这些边都达到上界,那么答案就是上界减去重心到子节点的边权中最小的就是了

//quming
#include<bits/stdc++.h>
#define R register
#define pb emplace_back
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
typedef pair<ll,int> pi;
const int N=1e5+5;
struct eg{int v,nx,w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R int w){e[++tot]={v,head[u],w},head[u]=tot;}
inline int min(R int x,R int y){return x<y?x:y;}
ll res;int sz[N],rt,rv,op,mmx,n;
void dfs(int u,int fa,ll dis){
	sz[u]=1;
	R int mx=0;
	go(u)if(v!=fa){
		dfs(v,u,dis+e[i].w),sz[u]+=sz[v];
		res+=1ll*min(sz[v],n-sz[v])*e[i].w<<1;
		if((sz[v]<<1)==n)op=1,rv=e[i].w;
		cmax(mx,sz[v]);
	}
	cmax(mx,n-sz[u]);
	if(cmin(mmx,mx))rt=u;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d",&n),mmx=n+1;
	for(R int i=1,u,v,w;i<n;++i){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w),add(v,u,w);
	}
	dfs(1,0,0);
	if(op&1)res-=rv;
	else{
		R int mn=0x3f3f3f3f;
		go(rt)cmin(mn,e[i].w);
		res-=mn;
	}
	printf("%lld
",res);
	return 0;
}

(E)

越来越觉得自己好蠢了……

我们先从原点开始考虑,设(c(x,y))表示从原点到(x,y)的方案数((xgeq 0,ygeq 0)),这个显然为({x+ychoose x})

然后开始推倒

[egin{aligned} sum_{y=0}^Y c(X,y)=c(X+1,Y) end{aligned} ]

就是说走到((X+1,Y))的方案,就等价于我们枚举在第(X)行的哪一列往下走再往右

[egin{aligned} sum_{x=0}^Xsum_{y=0}^Y c(x,y)=c(X+1,Y+1)-1 end{aligned} ]

用前面那个柿子带进去就可以了

[egin{aligned} sum_{x=X_1}^{X_2}sum_{y=Y_1}^{Y_2} c(x,y)=C(X_2+1,Y_2+1)-c(X_1,Y_2+1)-c(X_2+1,Y_1)+c(X_1,Y_1) end{aligned} ]

相当于是子矩阵求和,那么变成四个前缀和减一减就好了

然后我们发现一个矩阵可以拆成四个单点求和的形式!

然后我们考虑,起点,中间点,终点的范围都是一个矩阵,如果我们枚举中间点,那么就可以把起点和终点的求和给拆成四个单点的形式,这样就可以(O(16))计算一个中间点的贡献了

考虑如果一条路径经过中间点的那个矩阵,那么贡献就是这条路径和矩阵相交的点的个数,也就是说这条路径要乘上这个个数

我们枚举这条路径是从哪里进矩阵的,从哪里出矩阵的,假设分别是从((x,y))进且从((xx,yy))出,那么贡献就是(xx-x+yy-y+1)

那么我们只枚举路径进矩阵的位置,对于路径数乘上((-x-y)),再枚举路径出矩阵的位置,对于路径数乘上(xx+yy+1),这样我们发现每条合法的路径都会被计算恰好贡献次,所以就没问题了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=1e9+7;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=2e6+5;
int fac[N],ifac[N];
void init(int n=2e6){
	fac[0]=ifac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],P-2);fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
}
inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
inline int calc(R int x,R int y,R int xx,R int yy){
	R int res=C(x+y,x);
	upd(res,C(xx+1+yy+1,xx+1));
	upd(res,P-C(x+yy+1,x));
	upd(res,P-C(y+xx+1,y));
	return res;
}
int x[15],y[15],res;
inline int t1(R int xx,R int yy){
	return calc(xx-x[2],yy-y[2],xx-x[1],yy-y[1]);
}
inline int t2(R int xx,R int yy){
	return calc(x[5]-xx,y[5]-yy,x[6]-xx,y[6]-yy);
}
int main(){
//	freopen("testdata.in","r",stdin);
	init();
	fp(i,1,6)scanf("%d",&x[i]);
	fp(i,1,6)scanf("%d",&y[i]);
	fp(i,x[3],x[4]){
		upd(res,1ll*(P-y[3]-i)*t1(i,y[3]-1)%P*t2(i,y[3])%P);
		upd(res,1ll*(y[4]+1+i)*t1(i,y[4])%P*t2(i,y[4]+1)%P);
	}
	fp(j,y[3],y[4]){
		upd(res,1ll*(P-x[3]-j)*t1(x[3]-1,j)%P*t2(x[3],j)%P);
		upd(res,1ll*(x[4]+1+j)*t1(x[4],j)%P*t2(x[4]+1,j)%P);
	}
	printf("%d
",res);
	return 0;
}

(F)

首先根据每个点的子节点的个数,是可以判断该节点需要填奇数还是偶数的,如果这个奇偶性在两棵树上不同那么显然无解了

否则的话,对于偶节点,我们令其为(0),对于每个奇节点的子树中一定有(2k+1)个奇节点,我们把这些搞成(k)组匹配,对于((u,v))的一组匹配,令(a_u=1,a_v=-1)就行了这样子树中的总和的绝对值一定是(1)

而对于我们连的边,它一定是一个二分图(否则就会有一个点连了两条边且来自同一棵子树),那么直接黑白染色确定方案就行了

//quming
#include<bits/stdc++.h>
#define R register
#define pb emplace_back
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
vector<int>t1[N],t2[N];
int a1[N],a2[N],st[N],b[N],rt1,rt2;
int n,top;
void dfs1(int u){
	for(auto v:t1[u])dfs1(v);
	if(a1[u])st[++top]=u;
	while(top>=2){
		add(st[top],st[top-1]),add(st[top-1],st[top]);
		top-=2;
	}
}
void dfs2(int u){
	for(auto v:t2[u])dfs2(v);
	if(a2[u])st[++top]=u;
	while(top>=2){
		add(st[top],st[top-1]),add(st[top-1],st[top]);
		top-=2;
	}
}
void dfs(int u,int c){
	b[u]=c;
	go(u)if(!b[v])dfs(v,-c);
}
int main(){
	scanf("%d",&n);
	fp(i,1,n)a1[i]=a2[i]=1;
	for(R int i=1,fa;i<=n;++i){
		scanf("%d",&fa);
		if(fa==-1)rt1=i;else t1[fa].pb(i),a1[fa]^=1;
	}
	for(R int i=1,fa;i<=n;++i){
		scanf("%d",&fa);
		if(fa==-1)rt2=i;else t2[fa].pb(i),a2[fa]^=1;
	}
	fp(i,1,n)if(a1[i]!=a2[i])return puts("IMPOSSIBLE"),0;
	puts("POSSIBLE");
	top=0,dfs1(rt1);
	top=0,dfs2(rt2);
	fp(i,1,n)if(a1[i]&&!b[i])dfs(i,1);
	fp(i,1,n)printf("%d ",b[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/yuanquming/p/11593704.html