• AtCoder Grand Contest 012题解


    传送门

    (A)

    肯定是后面每两个陪最前面一个最优

    typedef long long ll;
    const int N=5e5+5;
    int a[N],n;ll res;
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n*3)scanf("%d",&a[i]);
    	sort(a+1,a+1+n*3);
    	for(R int i=n+1;i<=n*3;i+=2)res+=a[i];
    	printf("%lld
    ",res);
    	return 0;
    }
    

    (B)

    首先染色肯定是后面的覆盖前面的,那么我们从后往前做,并对每个点记录(d_i)从这个点还能延伸多少,只有当(d_i)增大的时候才考虑从它开始更新,因为所有的(d)都很小,所以每个点被更新不会超过(d_i)次,总复杂度为(O(d_in))

    //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=1e5+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;}
    struct node{int u,d,c;}E[N];
    int d[N],c[N],q[N],n,m,Q;
    void solve(int S,int dis,int col){
    	int h=1,t=0,u;
    	q[++t]=S;
    	while(h<=t){
    		u=q[h++];if(!c[u])c[u]=col;
    		go(u)if(cmax(d[v],d[u]-1))q[++t]=v;
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	fp(i,1,n)d[i]=-1;
    	for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
    	scanf("%d",&Q);
    	fp(i,1,Q)scanf("%d%d%d",&E[i].u,&E[i].d,&E[i].c);
    	fd(i,Q,1)if(cmax(d[E[i].u],E[i].d))solve(E[i].u,E[i].d,E[i].c);
    	fp(i,1,n)printf("%d
    ",c[i]);
    	return 0;
    }
    

    (C)

    让最终的序列长成形如(p_1,p_2,p_3,...,p_n,1,2,3,...,n)的形式,那么发现合法的子序列个数就是(p)中递增子序列个数(-1)

    那么我们(++n),然后用一个类似倍增的思想来维护。发现空集时递增子序列个数为(1),在后面加上(n+1)子序列个数( imes 2),在前面加上(n+1)子序列个数(+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;
    typedef long long ll;
    deque<int>st;ll n,k;int pos;
    int main(){
    	scanf("%lld",&n);++n;
    	while((1ll<<pos)<=n)++pos;
    	fd(i,pos-2,0){
    		st.push_back(++k);
    		if(n>>i&1)st.push_front(++k);
    	}
    	printf("%d
    ",k<<1);
    	fp(i,0,k-1)printf("%d ",st[i]);
    	fp(i,1,k)printf("%d ",i);
    	return 0;
    }
    

    (D)

    首先,我们发现如果能交换(ab)(ac),那么可以在不改变其他位置的情况下交换(bc),所以如果我们把所有可以交换的连一条边,则一个连通块中所有的点都可以互换位置,连通块内部的答案直接算就可以了

    然后考虑连边,对于每个点(u),设它这种颜色对应的重量最小的点为(mn),有两种情况(u)能和(mn)联通,一种是直接连边,一种是找到一个与它们颜色不同的(v)然后连边((u,v))((v,mn))。那么如果(u eq mn)且能直接连边就连边,否则找到一个与它们颜色不同且重量最小的点(v)尝试与它连边。不难发现最终所有连过边的点都和重量最小的点在同一个连通块中,那么计算一下就行了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fi first
    #define se second
    #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=2e5+5;
    typedef pair<int,int> pi;
    int fac[N],ifac[N],fa[N],mn[N],cnt[N];pi c[N];
    int n,x,y,pos,sz,res;
    inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    int main(){
    	scanf("%d%d%d",&n,&x,&y);
    	fp(i,1,n)scanf("%d%d",&c[i].se,&c[i].fi);
    	sort(c+1,c+1+n);
    	pos=2;
    	while(pos<=n&&c[pos].se==c[1].se)++pos;
    	if(pos>n)return puts("1"),0;
    	fd(i,n,1)mn[c[i].se]=i,fa[i]=i;
    	fd(i,n,1){
    		if(i!=mn[c[i].se]&&c[i].fi+c[mn[c[i].se]].fi<=x)
    			fa[find(i)]=find(mn[c[i].se]);
    		else if(c[i].se!=c[1].se&&c[i].fi+c[1].fi<=y)
    			fa[find(i)]=find(1);
    		else if(c[i].se!=c[pos].se&&c[i].fi+c[pos].fi<=y)
    			fa[find(i)]=find(pos);
    	}
    	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);
    	fp(i,1,n)if(find(i)==find(1))++sz,++cnt[c[i].se];
    	res=fac[sz];
    	fp(i,1,n)res=mul(res,ifac[cnt[i]]);
    	printf("%d
    ",res);
    	return 0;
    }
    

    (F)

    感觉司公子说的比(yyb)说的好懂一点……那我这里就说一下司公子的写法吧……

    方便起见先默认(a_i=i)

    首先,显然(ileq b_ileq 2n-i)

    其次,我们考虑加入两个新的数字,那么中位数要么不变,要么变成前驱或者后继

    所以会有两个性质

    (1.ileq b_ileq 2n-i)

    (2.),不存在(i<j),使得(b_j<b_i<b_{j+1})或者(b_j>b_i>b_{j+1})

    我们可以认为假设有一个(S)表示所有合法的(b_i)的取值的集合,首先肯定有(b_n=n),那么如果我们倒着构造(b),且正在考虑到(b_i),第一个性质代表我们可以往(S)中加入(i)(2n-i),第二个性质说明如果选定了(b_i),那么(S)中所有值在(b_i)(b_{i+1})之间的数(不含端点)都应该被删去(因为根据性质(2)显然前面的数中是不能存在这样的数的)

    (ps:)或者上面的第二点也可以理解为,如果(b_{i} eq b_{i+1})说明(b_{i+1})必定是前(2i+1)个数中(b_i)的前驱或后继,那么在它们区间内的数显然会妨碍它们成为前驱或后继,所以在(a)中都不能存在

    然后我们来考虑一下,按照上面这个规则构造出来的(b)是否全部合法,也就是说(S)里面所有的数只有一个必要性,如何证明它们的充分性

    考虑从(b_{i+1})(b_i)的过程,如果(b_{i+1}>b_i),那么我们删去前(2i+1)个数中比(b_{i+1})大的最小的两个数,那么我们可以取到合法的(b_i)的最小值,同理如果(b_{i+1}<b_i)可以取到最大值,而(b_{i}=b_{i+1})的话,我们只要删掉前(2i+1)个数中比(b_i)大的最小的数和比(b_i)小的最大的数就可以取到最大最小值了

    然后接下来就是计算方案了,记(f[i][j][k])表示倒序考虑(b),考虑了(i)个数,合法的取值个数为(j),且取了这些值中第(k)个的方案数,转移枚举一下下一个数取了合法取值中第几个就行了

    记得有一个问题就是实际上(a)可能会有相等的情况,那么性质一能不能拓展要看情况

    说真的还是看代码比较好理解……

    //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=105;
    int a[N],f[N][N][N],n,res;
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n+n-1)scanf("%d",&a[i]);
    	sort(a+1,a+n+n);
    	f[1][1][1]=1;
    	fp(i,1,n-1)fp(j,1,n+n-1)fp(k,1,j)if(f[i][j][k]){
    		R int tj=j,tk=k;
    		if(a[n+i]!=a[n+i-1])++tj;
    		if(a[n-i]!=a[n-i+1])++tj,++tk;
    		fp(h,1,tj){
    			if(h<tk)upd(f[i+1][tj-(tk-h-1)][h],f[i][j][k]);
    			if(h>tk)upd(f[i+1][tj-(h-tk-1)][tk+1],f[i][j][k]);
    			if(h==tk)upd(f[i+1][tj][h],f[i][j][k]);
    		}
    	}
    	fp(i,1,n+n-1)fp(j,1,i)upd(res,f[n][i][j]);
    	printf("%d
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    日期间隔之年、月、日、时、分、秒
    加减年、月、日、时、分、秒
    求总和的百分比
    返回最值所在行数据
    返回各部门工资排名前三位的员工
    生成累计和
    将字符和数字数据分离
    从字符串中删除不需要的字符
    计算字符在字符串中出现的次数
    字符串文字中包含引号
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11524460.html
Copyright © 2020-2023  润新知