zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 031

    Preface

    这场后面题目好难啊,C就开始思博了


    A - Colorful Subsequence

    考虑DP,(f_i)表示前(i)个数的答案,考虑如何去除重复的限制

    对于当前的(i),设之前(s_j=s_i)(j)(c)个,显然我们在这(c+1)个数里只能选出一个来,因此转移(f_i+=frac{f_{i-1}}{c+1})

    然后我们发现数组都不用开,直接(O(n))做即可

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=100005,mod=1e9+7;
    int n,c[N],ans; char s[N];
    inline int quick_pow(int x,int p=mod-2,int mul=1)
    {
    	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
    }
    int main()
    {
    	RI i,j; for (scanf("%d%s",&n,s+1),ans=i=1;i<=n;++i)
    	++c[s[i]-'a'],(ans+=1LL*ans*quick_pow(c[s[i]-'a'])%mod)%=mod;
    	return printf("%d",ans-1),0;
    }
    
    

    B - Reversi

    (f_i)表示前(i)个石子染色的方案数,很容易发现我们对于某种颜色,我们只需要找出这个颜色的前驱

    考虑给这段区间染色,显然染过之后这段区间内的数都不会再染色了,因此我们可以直接从前驱转移来

    但这样可能会无法从前驱的前驱转移来,因此我们每次把同种颜色的贡献一起累加即可

    注意特判前驱不存在以及相同的颜色相邻的情况,复杂度(O(n))

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=200005,mod=1e9+7;
    int n,x,pre[N],f[N];
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    int main()
    {
    	RI i,j; for (scanf("%d",&n),f[0]=i=1;i<=n;pre[x]=i++)
    	if (scanf("%d",&x),f[i]=f[i-1],pre[x]&&pre[x]!=i-1) inc(f[i],f[pre[x]]);
    	return printf("%d",f[n]),0;
    }
    
    

    C - Differ by 1 Bit

    我们可以把所有数都(operatorname{xor})(A),这样就是找一条(0 o Aoperatorname{xor} B)的路径,最后再(operatorname{xor})回去即可

    首先考虑判断无解,我们发现每次操作会改变一位的值,换句话说会改变(1)的个数的奇偶性

    然后我们一共要做奇数次变换,因此若(Aoperatorname{xor} B)(1)的个数是奇数,那么才合法

    接下来考虑如何构造,我们考虑把问题抽象成在一个(n)维的超立方体的顶点之间走路,让你找出一条路径能走到目标点并且需要经过所有顶点

    很容易想到我们可以先把(n-1)维的超立方体走完,然后一步走到(n)维的上面去

    具体地,我们每次选出一个二进制位(p)满足(Aoperatorname{xor} B)在第(p)位上的值是(1)

    我们先遍历第(p)位为(0)的所有点组成的(n-1)维超立方体后,在去遍历第(p)位为(1)的所有点组成的(n-1)维超立方体

    显然在两个(n-1)维的超立方体间的连接点是任意取的,因此我们前面的判断是正确的

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=17;
    int n,a,b,lim;
    inline int count(CI x,int ret=0)
    {
    	for (RI i=0;i<n;++i) ret+=((x>>i)&1); return ret;
    }
    inline void DFS(CI x,CI y,CI cs)
    {
    	if (count(cs)==1) return (void)(printf("%d %d ",y,x^y));
    	for (RI i=0,j;i<n;++i) if (((x>>i)&1)&&((cs>>i)&1))
    	for (j=0;j<n;++j) if (((cs>>j)&1)&&i!=j)
    	return (void)(DFS(1<<j,y,cs^(1<<i)),DFS(x^(1<<i)^(1<<j),y^(1<<i)^(1<<j),cs^(1<<i)));
    }
    int main()
    {
    	scanf("%d%d%d",&n,&a,&b); if (count(a^b)&1)
    	puts("YES"),DFS(a^b,a,(1<<n)-1); else puts("NO"); return 0;
    }
    
    

    D - A Sequence of Permutations

    大力找规律题。我们首先发现关于(f(p,q)),设其为(c),则:

    [c_{p_i}=q_iLeftrightarrow c_i=q_{p'_i}\ s.t. p_x=iLeftrightarrow p'_i=x ]

    考虑我们记(c_i=q_{p'_i})(c=qp'),即我们定义其为关于置换群的一种运算

    经过实践检验我们发现它满足结合律,但不满足交换律

    同时我们发现((ab')'=ab',(ab'c)'=c'ba'),因此接下来我们来大力推导找规律

    [egin{align}&a_1=p\ &a_2=q\ &a_3=qp'\ &a_4=qp'q'\ &a_5=qp'q'pq'\ &a_6=qp'q'pq'qpq'\ &a_7=qp'q'pq'qpq'qp'qpq'\ &a_8=qp'q'pq'qpq'qp'qpq'qp'q'qp'qpq'\ &a_9=qp'q'pq'qpq'qp'qpq'qp'q'qp'qpq'qp'q'pq'qp'q'qp'qpq'\ &cdotsend{align} ]

    由于相邻的(pp')以及(qq')可以约去,因此我们改写一下:

    [egin{align}&a_1=p\ &a_2=q\ &a_3=qp'\ &a_4=qp'q'\ &a_5=qp'q'pq'\ &a_6=qp'q'ppq'\ &a_7=qp'q'ppp'qpq'\ &a_8=qp'q'pqp'qpq'\ &a_9=qp'q'pqp'p'qpq'\ &cdotsend{align} ]

    很容易发现若我们令(T=qp'q'p),则对于(n>6),均有(a_n=Ta_{n-6}T')

    证明可以用归纳法,这里略去我才不会说我是找规律出来的

    我们发现之前定义的运算和矩阵乘法有着相同的性质,因此我们可以用快速幂处理

    总复杂度(O(nlog k))

    #include<cstdio>
    #include<cstring>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=100005;
    int n,k;
    struct permutation
    {
    	int a[N];
    	inline permutation(void) { memset(a,0,sizeof(a)); }
    	inline int& operator [] (CI x) { return a[x]; }
    	inline friend permutation operator ~ (permutation A)
    	{
    		permutation tp; for (RI i=1;i<=n;++i) tp[A[i]]=i; return tp;
    	}
    	inline friend permutation operator * (permutation A,permutation B)
    	{
    		permutation C; for (RI i=1;i<=n;++i) C[i]=A[B[i]]; return C;
    	}
    	inline friend permutation operator ^ (permutation A,int p)
    	{
    		permutation t; for (RI i=1;i<=n;++i) t[i]=i;
    		for (;p;p>>=1,A=A*A) if (p&1) t=t*A; return t;
    	}
    	inline void print(void)
    	{
    		for (RI i=1;i<=n;++i) printf("%d ",a[i]);
    	}
    }a[10],T;
    int main()
    {
    	RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[1][i]);
    	for (i=1;i<=n;++i) scanf("%d",&a[2][i]);
    	for (i=3;i<=6;++i) a[i]=a[i-1]*(~a[i-2]);
    	int d=k/6,r=k%6; if (!r) --d,r=6; T=a[2]*(~a[1])*(~a[2])*a[1];
    	return ((T^d)*a[r]*((~T)^d)).print(),0;
    }
    
    

    E - Snuke the Phantom Thief

    很妙的网络流建模题,增长了有用的姿势

    首先我们考虑一维的问题怎么做,考虑对限制((a_i,b_i))进行转化

    一个比较显然的的结论,若限制(le a_i)的珠宝有(b_i)个,则选出的珠宝中第(b_i+1)大的一定(>a_i)

    然后考虑(le a_i)如何转化,直接做不方便,我们考虑枚举选出的珠宝总数(k)

    那么若限制(ge a_i)的珠宝有(b_i)个,则选出的珠宝中第(k-b_i)大的一定(<a_i)

    那么现在我们可以得到(k)个二元组((l_i,r_i)),表示选出的第(i)个珠宝的坐标范围,显然(l_i,r_i)均要满足单调性

    现在问题其实就是每个二元组可以在它能选择的珠宝中选择一个,每个二元组只能和一个珠宝匹配,很显然就是个最大权匹配问题

    那么现在两维的也很简单,我们把(n)个点拆成两份,分别表示横纵坐标,与对应的关于横纵坐标的(k)个点连边,同时(n)个点连((i,i'))的边表示这个珠宝只能选一次

    直接跑最大费用最大流即可,总复杂度(费用流O(n imes ext{费用流}))

    #include<cstdio>
    #include<cctype>
    #include<iostream>
    #include<queue>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=350,M=(N*N<<1)+(3*N);
    const long long INF=1e18;
    struct edge
    {
    	int to,nxt,v; long long c;
    }e[M<<1]; char opt[N]; long long ans,v[N];
    int n,m,head[N],cnt,x[N],y[N],L[N],R[N],U[N],D[N],a[N],b[N],s,t;
    inline char getch(void)
    {
    	char ch; while (!isalpha(ch=getchar())); return ch;
    }
    inline void addedge(CI x,CI y,CI v,const long long& c)
    {
    	e[++cnt]=(edge){y,head[x],v,c}; head[x]=cnt;
    	e[++cnt]=(edge){x,head[y],0,-c}; head[y]=cnt;
    }
    #define to e[i].to
    namespace NF //Network_Flow
    {
    	queue <int> q; int pre[N],lst[N],cap[N]; long long dis[N]; bool vis[N];
    	inline bool SPFA(CI s,CI t)
    	{
    		RI i; for (i=s;i<=t;++i) dis[i]=-INF,cap[i]=1e9,pre[i]=-1;
    		pre[s]=0; q.push(s); dis[s]=0; vis[s]=1; while (!q.empty())
    		{
    			int now=q.front(); q.pop(); vis[now]=0;
    			for (i=head[now];i;i=e[i].nxt)
    			if (e[i].v&&dis[to]<dis[now]+e[i].c)
    			{
    				dis[to]=dis[now]+e[i].c; pre[to]=now; lst[to]=i;
    				cap[to]=min(cap[now],e[i].v); if (!vis[to]) vis[to]=1,q.push(to);
    			}
    		}
    		return ~pre[t];
    	}
    	inline long long MCMF(CI s,CI t)
    	{
    		long long ret=0; while (SPFA(s,t))
    		{
    			ret+=dis[t]*cap[t]; for (int nw=t;nw;nw=pre[nw])
    			e[lst[nw]].v-=cap[t],e[lst[nw]^1].v+=cap[t];
    		}
    		return ret;
    	}
    	inline void clear(CI n)
    	{
    		cnt=1; for (RI i=0;i<=n;++i) head[i]=0;
    	}
    };
    #undef to
    inline long long calc(CI k)
    {
    	RI i,j; for (i=1;i<=k;++i) L[i]=D[i]=0,R[i]=U[i]=1e9;
    	for (NF::clear(2*n+2*k+1),i=1;i<=m;++i) if (b[i]<k) 
    	{
    		if (opt[i]=='L') L[b[i]+1]=a[i]+1;
    		if (opt[i]=='R') R[k-b[i]]=a[i]-1; 
    		if (opt[i]=='D') D[b[i]+1]=a[i]+1;
    		if (opt[i]=='U') U[k-b[i]]=a[i]-1; 
    	}
    	for (i=2;i<=k;++i) L[i]=max(L[i],L[i-1]),D[i]=max(D[i],D[i-1]);
    	for (i=k-1;i;--i) R[i]=min(R[i],R[i+1]),U[i]=min(U[i],U[i+1]);
    	for (s=0,t=2*n+2*k+1,i=1;i<=k;++i) addedge(s,i,1,0),addedge(k+2*n+i,t,1,0);
    	for (i=1;i<=n;++i) addedge(k+i,k+n+i,1,v[i]);
    	for (i=1;i<=k;++i) for (j=1;j<=n;++j)
    	{
    		if (L[i]<=x[j]&&x[j]<=R[i]) addedge(i,k+j,1,0);
    		if (D[i]<=y[j]&&y[j]<=U[i]) addedge(k+n+j,k+2*n+i,1,0);
    	}
    	return NF::MCMF(s,t);
    }
    int main()
    {
    	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d%lld",&x[i],&y[i],&v[i]);
    	for (scanf("%d",&m),i=1;i<=m;++i) opt[i]=getch(),scanf("%d%d",&a[i],&b[i]);
    	for (i=1;i<=n;++i) ans=max(ans,calc(i)); return printf("%lld",ans),0;
    }
    
    

    Postscript

    暑假要结束了,赶紧冲冲冲一波!

  • 相关阅读:
    使用Application Insights 做分析
    UWP中GridView右击选中的实现
    Bing Map
    UWP深入学习六:Build better apps: Windows 10 by 10 development series
    从上往下打印二叉树
    二叉树中和为某一值的路径
    二叉树的镜像
    树的子结构
    由前序遍历和中序遍历构建二叉树
    二叉树常见题目
  • 原文地址:https://www.cnblogs.com/cjjsb/p/13535692.html
Copyright © 2011-2022 走看看