AtCoder Grand Contest 013题解

传送门

\(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 N=2e5+5;
int a[N],tot,n,res;
int main(){
	scanf("%d",&n);
	fp(i,1,n)scanf("%d",&a[i]);
	fp(i,1,n)if(a[i]!=a[i-1])a[++tot]=a[i];
	n=tot,res=1;
	for(R int l=1,r=2;r<=n;l=r++){
		if(a[l]<a[r])while(r<=n&&a[r]>a[r-1])++r;
			else while(r<=n&&a[r]<a[r-1])++r;
		if(r<=n)++res;
	}
	printf("%d\n",res);
	return 0;
}

\(B\)

随便找一个点开始\(dfs\)出一条链一直到不能\(dfs\)为止,那么发现这条链的末尾必定满足所有与它相连的点都在路径中。那么找出两条这样的链拼起来就可以了

//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=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;}
int st[N],vis[N],top,n,m;
void dfs(int u){vis[u]=1;go(u)if(!vis[v]){st[++top]=v,dfs(v);break;}}
int main(){
	scanf("%d%d",&n,&m);
	for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
	st[++top]=1,dfs(1),reverse(st+1,st+1+top),dfs(1);
	printf("%d\n",top);
	fp(i,1,top)printf("%d ",st[i]);
	return 0;
}

\(C\)

首先如果我们认为碰撞之后继续走,那么所有的最终位置都可以算出来。而因为按原来的走法相对顺序是不变的,所以我们只要推出\(1\)号点的位置就可以依次推出其他点的位置

一开始时一号蚂蚁坐标最小,每当有蚂蚁从\(L-1\)爬到\(0\),一号蚂蚁的排名就\(+1\),反之如果有蚂蚁从\(0\)爬到\(L-1\),一号蚂蚁排名就\(-11\),对于每个点都计算一下就可以算出一号蚂蚁最终的排名了

//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;
const int N=2e5+5;
int a[N],pos[N],n,L,T,x,w;ll t;
int main(){
	scanf("%d%d%d",&n,&L,&T);
	fp(i,0,n-1){
		scanf("%d%d",&x,&w),w=(w==2?-1:1);
		pos[i]=((x+T*w)%L+L)%L;
		if(w==1)t+=(x+T)/L;else t-=(L-1-x+T)/L;
	}
	t=(t%n+n)%n;
	sort(pos,pos+n);
	fp(i,t,n-1)printf("%d\n",pos[i]);
	fp(i,0,t-1)printf("%d\n",pos[i]);
	return 0;
}

\(D\)

首先第\(1\)个球和第\(2m\)个球显然颜色任选,那么我们去掉这两个,变成了\(m-1\)次放两个球选两个球的操作,同时初始时有\(n-1\)个球

\(1\)表示黑球,\(0\)表示白球,那么每次放球有三种选择,\(11,00,01\)\(01\)\(10\)默认为同一种,到时候方案数\(\times 2\)就行了)。对于原来的袋中,球的总数不变,如果只考虑黑球的个数,那么三种选择分别相当于\(+1,-1,0\)。我们用\(f[i][j]\)表示考虑了前\(i\)个位置且黑球有\(j\)个,只要合法就转移就是了

然而有一个问题就是如果序列合法时袋子里剩下球不同会被当做不一样的方案,导致重复计算,一个方法是我们减去袋中原有小于等于\(n-2\)个球时的答案,这是因为对于每一种合法的序列必然存在一个初始的袋中的球的集合卡到上界的方案(要么是白球数量卡到上界,要么是黑球数量卡到上界),那么在\(n-2\)的情况中所有这些方案都不会被计算,所以减去就能得到正确的方案了

//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=3005;
int f[N][N][2],n,m,res;
int main(){
	scanf("%d%d",&n,&m),--n,--m;
	f[0][0][1]=1;
	fp(i,1,n)f[0][i][0]=1;
	fp(i,0,m-1)fp(j,0,n)fp(k,0,1)if(f[i][j][k]){
		if(j)upd(f[i+1][j-1][k|(j==1)],f[i][j][k]);
		if(j<n)upd(f[i+1][j+1][k],f[i][j][k]);
		upd(f[i+1][j][k],add(f[i][j][k],f[i][j][k]));
	}
	fp(i,0,n)upd(res,f[m][i][1]);
	printf("%d\n",mul(res,4));
	return 0;
}

\(E\)

首先,写出暴力是很容易的,而且优化到\(O(n)\)也是很容易的,不过如果有能继续优化到能过的话请在下面提醒一下我怎么做

这里有一个套路,就是把平方和转化成有序对,即我们可以把原问题转化成初始时有\(n\)个格子,\(1\)格子左边和\(n\)格子右边强制有隔板,中间隔板随便插,每个格子上可以随便放\(0/1\)两种颜色的球,要求每两个隔板之间两种颜色的球出现且仅出现一次,求方案数

这个的话,我们设\(f[i][j]\)表示考虑到第\(i\)个格子是否放球以及第\(i-1\)\(i\)之间是否放隔板,且上一个隔板到\(i\)之间共有\(j\)个球,最后的答案就是\(f[n][2]\)。我们发现对于所有\(i-1\)\(i\)之间能放隔板的格子,转移是一样的,而所有不能放的也是一样的,所以我们可以用一个\(3\times 3\)的矩阵来表示这一个转移,最后矩乘优化即可

先给出暴力的代码(这是\(yyb\)聚聚的)

f[0][0]=1;
for(int i=1,pos=1;i<=n;++i)
{
    if(i!=a[pos])
        for(int j=0;j<3;++j)
            add(f[i][j],((j==1)?2:1)*f[i-1][2]%MOD);
    for(int j=0;j<3;++j)
    {
        add(f[i][j],f[i-1][j]);
        if(j<2)add(f[i][j+1],(2-j)*f[i-1][j]%MOD);
        if(j<1)add(f[i][j+2],f[i-1][j]);
    }
    if(i==a[pos])++pos;
}
printf("%d\n",f[n][2]);

然后是这题的代码

//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=2e5+5;
struct Matrix{
	int a[3][3];
	inline Matrix(){memset(a,0,sizeof(a));}
	inline int* operator [](const int &x){return a[x];}
	inline Matrix operator *(Matrix b){
		Matrix c;
		fp(i,0,2)fp(k,0,2)fp(j,0,2)upd(c[i][j],mul(a[i][k],b[k][j]));
		return c;
	}
}bin[35],A,B,ans;
int a[N],n,m;
inline Matrix ksm(R int x){
	Matrix c;c[0][0]=c[1][1]=c[2][2]=1;
	for(R int i=0;x;x>>=1,++i)if(x&1)c=c*bin[i];
	return c;
}
int main(){
	scanf("%d%d",&n,&m);
	fp(i,1,m)scanf("%d",&a[i]),++a[i];
	A[0][0]=1,A[0][1]=2,A[0][2]=1;
	A[1][0]=0,A[1][1]=1,A[1][2]=1;
	A[2][0]=1,A[2][1]=2,A[2][2]=2;
	B=A,B[2][0]=0,B[2][1]=0,B[2][2]=1;
	bin[0]=A;fp(i,1,30)bin[i]=bin[i-1]*bin[i-1];
	ans[0][0]=ans[1][1]=ans[2][2]=1;
	fp(i,1,m)ans=ans*ksm(a[i]-a[i-1]-1)*B;
	ans=ans*ksm(n-a[m]);
	printf("%d\n",ans[0][2]);
	return 0;
}

\(F\)

首先我们把\(C\)\(sort\)之后重新标号为\(1,2,...,n+1\),然后对于每个\(a\)用最小的\(i\)满足\(a\leq c_i\)\(i\)来表示,对\(b\)也同样这样离散

先假设所有牌都翻到正面是否可行,我们初始时令一个\([1,n+1]\)的数列全部为\(0\),且令每一个\([i,n+1]-1\),每一个\([a_i,n+1]+1\),如果最终数列中所有数都大于等于\(0\)证明可行。而对于询问,我们可以分别考虑是选择\(d\)还是\(e\),取较优的那个即可

对于询问,假设当前选择了\(x\),那么序列要满足\([x,n+1]\)全都大于等于\(-1\)且前面都大于等于\(0\)

对于一对\(a,b\),如果\(b<a\),那么我们可以通过翻转使得\([b,a-1]+1\),然后减掉一个正面的个数。如果把所有的区间取出来,我们从后往前做,对于每一个小于\(-1\)的位置,找到覆盖它的且左端点最左的一个区间,容易证明最优方案里这个区间一定会被选择翻转,那么我们把它翻转,然后正面朝上个数\(-1\),并删掉这个区间就是了

然后再从右往左做一遍,每次对于小于\(0\)的位置找到覆盖它且右端点最右的区间,同样选择翻转这个区间就是了。此时\([1,i]\)中所有元素都大于等于\(0\)\([i+1,n+1]\)都大于等于\(-1\),那么\(x=i+1\)的答案就出来了

//quming
#include<bits/stdc++.h>
#define R register
#define fi first
#define se second
#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 pair<int,int> pi;
const int N=2e5+5,inf=0x3f3f3f3f;
int a[N],b[N],c[N],d[N],f[N];
int n,Q,tot;
priority_queue<pi,vector<pi>,greater<pi> >h;
priority_queue<pi>q;
vector<int>li[N],ri[N];
inline void chg(R int x,R int y){for(;x<=n+1;x+=x&-x)d[x]+=y;}
inline int query(R int x){R int res=0;for(;x;x-=x&-x)res+=d[x];return res;}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d",&n);
	fp(i,1,n)scanf("%d%d",&a[i],&b[i]);
	fp(i,1,n+1)scanf("%d",&c[i]);
	sort(c+1,c+n+1+1);
	fp(i,1,n){
		a[i]=lower_bound(c+1,c+n+1+1,a[i])-c;
		b[i]=lower_bound(c+1,c+n+1+1,b[i])-c;
	}
	fp(i,1,n+1)chg(i,-1);fp(i,1,n)chg(a[i],1);
	fp(i,1,n)if(b[i]<a[i])ri[a[i]-1].pb(b[i]);
	fd(i,n+1,0){
		for(auto v:ri[i])h.push(pi(v,i));
		while(query(i)<-1){
			if(h.empty()||h.top().fi>i){tot=inf;break;}
			chg(h.top().fi,1),chg(h.top().se+1,-1),h.pop(),++tot;
		}
		if(tot>=inf){
			scanf("%d",&Q);
			fp(i,1,Q)puts("-1");
			return 0;
		}
	}
	while(!h.empty())li[h.top().fi].pb(h.top().se),h.pop();
	fp(i,0,n+1){
		for(auto v:li[i])q.push(pi(v,i));
		while(query(i)<0){
			if(q.empty()||q.top().fi<i){tot=inf;break;}
			chg(q.top().se,1),chg(q.top().fi+1,-1),q.pop(),++tot;
		}
		f[i]=tot;
		if(tot>=inf){
			fp(k,i,n+1)f[k]=inf;
			break;
		}
	}
	scanf("%d",&Q);
	for(R int d,e,res;Q;--Q){
		scanf("%d%d",&d,&e);
		d=lower_bound(c+1,c+n+1+1,d)-c;
		e=lower_bound(c+1,c+n+1+1,e)-c;
		res=-1,cmax(res,n+1-f[d-1]),cmax(res,n-f[e-1]);
		printf("%d\n",res);
	}
	return 0;
}
posted @ 2019-09-19 19:07  源曲明  阅读(152)  评论(0编辑  收藏  举报