zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    oc之数组反序输出示例
    OC--有这么一个 整数 123456789,如何将这个整数的每一位数,从末位开始依次放入数组中,并遍历 倒序输出字符串
    oc--截取字符串(从网址中截取用户名和密码)
    iOS 第七期考核题(字符串以及字典的使用 数组内容转换成字符串)
    iOS 第六期考核题(字典的使用)
    iOS 第五期考核题(字典与数组嵌套,字典的排序/删除)
    Linux服务启动报错日志分析
    新手Linux命令-1
    新手Linux命令-2
    计划任务服务
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11593704.html
Copyright © 2011-2022 走看看