• AtCoder Grand Contest 001


    AtCoder Grand Contest 001

    A - BBQ Easy

    翻译

    给你(2n)个数,需要两两配对成(n)对,每对的权值定义为两个数的较小值,求最大权值和。

    题解

    排序即可。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 202
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int ans,n,a[MAX<<1];
    int main()
    {
    	n=read();
    	for(int i=1;i<=n+n;++i)a[i]=read();
    	sort(&a[1],&a[n+n+1]);
    	for(int i=1;i<=n+n;i+=2)ans+=a[i];
    	printf("%d
    ",ans);
    	return 0;
    }
    

    B - Mysterious Light

    翻译

    见洛谷

    题解

    大概画一个图,发现其实每次都等价于把一个(60°)平行四边形分解成若干个变成等于短边的等边三角形。
    如果多出来了一部分,发现在干的事情是等价的,所以直接递归做就行了。
    我交了几遍一直只有部分分,发现递归的函数带的参定义为了(int)。以后这种问题还是要注意。

    #include<iostream>
    using namespace std;
    #define ll long long
    ll n,x,ans;
    ll calc(ll n,ll x)
    {
    	if(!x)return -n;
    	ll d=n%x;
    	return calc(x,d)+(n/x)*2*x;
    }
    int main()
    {
    	cin>>n>>x;ans=n;
    	ans+=calc(n-x,x);
    	cout<<ans<<endl;
    	return 0;
    }
    

    C - Shorten Diameter

    翻译

    见洛谷

    题解

    为什么这么傻逼的题目我都不会做。。。果然菜的不行啊。
    一个简单的想法就是我们钦定一个点作为根节点,然后删掉所有深度大于(K/2)的点。
    如果(K)是奇数的时候就不能直接这么做,我们就钦定一条边,然后把这条边连接的两个点当成两棵子树,同样不准有点的深度大于(K/2)就好了。
    时间复杂度(O(n^2)),为什么我就不会做呢?我现在真是蠢得不行啊。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define ll long long
    #define MAX 2002
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Line{int v,next;}e[MAX<<1];
    int h[MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    int n,K,dep[MAX],tot,ans=1e9;
    void dfs(int u,int ff)
    {
    	dep[u]=dep[ff]+1;
    	if(dep[u]>K/2)++tot;
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].v!=ff)dfs(e[i].v,u);
    }
    int main()
    {
    	n=read();K=read();
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();
    		Add(u,v);Add(v,u);
    	}
    	dep[0]=-1;
    	if(K%2==0)
    		for(int i=1;i<=n;++i)tot=0,dfs(i,0),ans=min(ans,tot);
    	else
    		for(int u=1;u<=n;++u)
    			for(int i=h[u];i;i=e[i].next)
    				dep[e[i].v]=-1,tot=0,dfs(u,e[i].v),dep[u]=-1,dfs(e[i].v,u),ans=min(ans,tot);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    D - Arrays and Palindrome

    翻译

    有两个和为(N)的数列({a},{b})
    对于任意一个满足以下两个条件的长度为(N)的串(S)

    • (a_1)个字符组成的串是回文串,接下来的(a_2)个字符组成的串是回文串,接下来(a_3)个......
    • (b_1)个字符组成的串是回文串,接下来的(b_2)个字符组成的串是回文串,接下来(b_3)个......
      都满足(S)的所有字符都相等。
      给定一个长度为(M)的数列(A),并且已知(a)(A)的一个排列。构造一个满足条件的(b)

    题解

    如果只考虑其中一个数列,那么能够得到的信息是一系列的相等关系,那么,再通过错位的相等显然就可以得到一系列的连等关系。换种说法,就是把所有相等关系连起来,那么这些变恰好能够让你一笔画。
    一笔画的条件就很好判断了,奇度点的个数不能超过(2),什么情况下会出现奇度点?一个点会被少连一次当且仅当恰好在某一个奇数回文串的正中间。而如果一个点在两个序列的限制条件中都是自己连向自己的话,显然不联通从而无解。
    那么就很好办了,只需要把(A)中的奇数段找出来,数下个数,如果合法,一个丢前面一个丢后面,然后构造(B)的时候只需要让他们错位就好了。
    可以看看题解里面画的图。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 100100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int a[MAX],n,m,S[MAX],top;
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=m;++i)
    	{
    		a[i]=read();
    		if(a[i]&1)S[++top]=i;
    	}
    	if(top>2){puts("Impossible");return 0;}
    	if(top)swap(a[1],a[S[1]]);
    	if(top>1)swap(a[m],a[S[2]]);
    	if(m==1)
    	{
    		if(a[1]==1)printf("1
    1
    1
    ");
    		else printf("%d
    2
    %d 1
    ",a[1],a[1]-1);
    		return 0;
    	}
    	for(int i=1;i<=m;++i)printf("%d ",a[i]);puts("");
    	printf("%d
    ",m-(a[m]==1));
    	printf("%d ",a[1]+1);
    	for(int i=2;i<m;++i)printf("%d ",a[i]);
    	if(a[m]>1)printf("%d ",a[m]-1);
    	puts("");return 0;
    }
    

    E - BBQ Hard

    翻译

    洛谷

    翻译其实有点问题。

    应该是

    [sum_{i=1}^nsum_{j=i+1}^nC_{a[i]+b[i]+a[j]+b[j]}^{a[i]+a[j]} ]

    题解

    这题可以说非常妙了。

    我们可以把这个值看做在网格图上的一点((-a[i],-b[i]))走到((a[j],b[j]))的方案数。
    而网格图走的方案数可以直接递推得到。
    那么我们对于每个点把它的坐标取反到第三象限,然后对于整个坐标系计算走到每一个格子的总方案。
    把所有((a[i],b[i]))的答案累加,再减去自己到自己的方案数,最后除二就是答案了。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    #define MAX 200200
    #define MOD 1000000007
    void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
    const int py=2010;
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int a[MAX],b[MAX],n,ans;
    int f[4500][4500];
    int inv[9000],jc[9000],jv[9000];
    int C(int n,int m){return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)a[i]=read(),b[i]=read();
    	for(int i=1;i<=n;++i)f[py-a[i]][py-b[i]]+=1;
    	for(int i=1;i<=py*2;++i)
    		for(int j=1;j<=py*2;++j)
    			add(f[i][j],f[i-1][j]),add(f[i][j],f[i][j-1]);
    	inv[0]=inv[1]=jc[0]=jv[0]=1;
    	for(int i=1;i<py<<2;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    	for(int i=2;i<py<<2;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<py<<2;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    	for(int i=1;i<=n;++i)add(ans,f[a[i]+py][b[i]+py]);
    	for(int i=1;i<=n;++i)add(ans,MOD-C(2*(a[i]+b[i]),2*a[i]));
    	ans=1ll*ans*inv[2]%MOD;printf("%d
    ",ans);
    	return 0;
    }
    
    

    F - Wide Swap

    翻译

    有一个长度为(n)的排列(P),对于满足(|i-j|ge K)(i,j),如果(|P_i-P_j|=1),那么可以交换(P_i,P_j)

    求可能的最小字典序排列。

    题解

    神仙题我都只会看题解.jpg

    发现(K)是一个很蛋疼的东西,于是转化一下(反正我不会.jpg),令(a_{P_i}=i),得到了一个排列(a_i)。这样子问题等价于变成了,每次可以交换相邻两个位置,并且他们的差的绝对值要大于等于(K)。这样子性质优秀很多,首先我们自己yy一下,认为(P)的字典序要最小,等价于(a)的字典序要最小(似乎字典序最小和最小的数尽可能在前面是一样的?)。接着再考虑一下(a)的交换关系,如果两个数不能交换,那么他们两个的相对位置永远不会变,当且这个数和后面的所有数的相对位置也不可能改变了。相对位置确定了,如果没有确定的显然就是没有限制,那么一遍拓扑排序就可以搞定。

    然而这样子的复杂度在最坏情况下边数是(O(n^2))的。大概是多了些什么边呢?比如说(x,y,z)三个数,(,x ightarrow y,x ightarrow z,y ightarrow z),那么显然(x ightarrow z)边是没有意义的。显然,对于任意一个(a_i),我们连边的范围一定是([a_{i-k},a_{i+k}])之间的,上面那个式子告诉我们,显然只需要连向最近的一个点就可以了(比如之前那个连边,显然顺序是(x-y-z)),这样子用一个线段树找最近的位置就好了?

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define MAX 500500
    #define lson (now<<1)
    #define rson (now<<1|1)
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,K;
    struct Line{int v,next;}e[MAX<<1];
    int h[MAX],cnt=1,dg[MAX];
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;dg[v]++;}
    int a[MAX],P[MAX],ans[MAX],tot;
    int t[MAX<<2];
    priority_queue<int,vector<int>,greater<int> >Q;
    void Modify(int now,int l,int r,int p,int w)
    {
    	if(l==r){t[now]=w;return;}
    	int mid=(l+r)>>1;
    	if(p<=mid)Modify(lson,l,mid,p,w);
    	else Modify(rson,mid+1,r,p,w);
    	t[now]=min(t[lson],t[rson]);
    }
    int Query(int now,int l,int r,int L,int R)
    {
    	if(L>R)return t[0];
    	if(l==L&&r==R)return t[now];
    	int mid=(l+r)>>1;
    	if(R<=mid)return Query(lson,l,mid,L,R);
    	if(L>mid)return Query(rson,mid+1,r,L,R);
    	return min(Query(lson,l,mid,L,mid),Query(rson,mid+1,r,mid+1,R));
    }
    void Topsort()
    {
    	for(int i=1;i<=n;++i)if(!dg[i])Q.push(i);
    	while(!Q.empty())
    	{
    		int u=Q.top();Q.pop();ans[u]=++tot;
    		for(int i=h[u];i;i=e[i].next)
    			if(!--dg[e[i].v])Q.push(e[i].v);
    	}
    }
    int main()
    {
    	n=read();K=read();
    	for(int i=1;i<=n;++i)a[P[i]=read()]=i;
    	memset(t,63,sizeof(t));
    	for(int i=n;i;--i)
    	{
    		int x=Query(1,1,n,a[i]+1,min(a[i]+K-1,n));
    		if(x<1e9)Add(a[i],a[x]);
    		x=Query(1,1,n,max(1,a[i]-K+1),a[i]-1);
    		if(x<1e9)Add(a[i],a[x]);
    		Modify(1,1,n,a[i],i);
    	}
    	Topsort();
    	for(int i=1;i<=n;++i)printf("%d
    ",ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    vue 购买弹出框 动画
    vue 和animate.css 的动画使用
    获得url地址?后的参数
    Java 实现随机数组元素升降序
    java for循环实现九九乘法表
    java 随机生成字符串验证码
    Mysql插入值时,避免重复插入
    Mysql的unique和primary key
    2020 3.6日电话面试(某外包公司)
    Intellij IDEA配置javaweb项目
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9626236.html
Copyright © 2020-2023  润新知