AtCoder Regular Contest 110

Contest Link

下午的 AtCoder 时好时坏 晚上的 AtCoder 赛活神仙

A - Redundant Redundancy

给定一个整数 \(N(2\leq N\leq 30)\) ,求一个整数 \(x(N\leq x\leq 10^{13})\) 使得对于任意的整数 \(y(2\leq y\leq N)\)\(x\bmod y=1\) .

Thoughts & Solution

签到题。 一个可行解是:

\[ans=\prod_{j=2}^nj+1 \]

//Author: RingweEH
ll gcd( ll a,ll b )
{
	return (b==0) ? a : gcd( b,a%b );
}

int main()
{
	ll n=read(),x=1;
	for ( ll i=2; i<=n; i++ )
	{
		ll gc=gcd( x,i ); x=x*i/gc;
	}
	printf( "%lld\n",x+1 );
}

B - Many 110

\(S\)110 重复 \(10^{10}\) 的结果。给定一个长度为 \(N\) 的字符串 \(T\) ,求其出现次数。

Thoughts & Solution

小型分讨。

  • 对于 \(n\leq 2\) ,直接判断输出
  • 对于 \(n>2\) ,根据 \(T\) 的前三个字符判断是从 \(S\) 串的 1/2/3 位开始,然后构造一个能恰好包含 \(T\) 的(长度)的 110\(str\) ,暴力匹配;如果可以,那么设 \(str\) 中使用了 \(x\)110 ,答案就是 \(10^{10}-(x-1)\) .否则答案为 \(0\) .如果根据前三位无法构造也是 \(0\) .
  • 具体见代码.
//Author: RingweEH
const int N=2e5+10;
int n;
char s[N],str[N+10];

int main()
{
	n=read(); scanf( "%s",s+1 );
	ll cnt=n/3+2;
	for ( int i=1; i<=cnt*3; i+=3 )
	{
		str[i]='1'; str[i+1]='1'; str[i+2]='0';
	}
	if ( n<=2 )
	{
		if ( n==1 )
		{
			if ( s[1]=='0' ) printf( "10000000000\n" );
			else printf( "20000000000\n" );
			return 0;
		}
		if ( (s[1]=='1') && (s[2]=='1') ) printf( "10000000000\n" );
		if ( (s[1]=='1') && (s[2]=='0') ) printf( "10000000000\n" );
		if ( (s[1]=='0') && (s[2]=='0') ) printf( "0\n" );
		if ( (s[1]=='0') && (s[2]=='1') ) printf( "9999999999\n" );
		return 0;
	}
	if ( (s[1]=='1') && (s[2]=='1') && (s[3]=='0') )
	{
		for ( int i=1; i<=n; i++ )
			if ( s[i]!=str[i] ) { printf( "0\n" ); return 0; }
		if ( n%3 ) cnt=n/3+1;
		else cnt=n/3;
		printf( "%lld\n",10000000000-(cnt-1) ); return 0;
	}
	else if ( (s[1]=='1') && (s[2]=='0') && (s[3]=='1') )
	{
		for ( int i=2; i<=n+1; i++ )
			if ( str[i]!=s[i-1] ) { printf( "0\n" ); return 0; }
		cnt=((n+1)%3) ? (n+1)/3+1 : (n+1)/3;
		printf( "%lld\n",10000000000-(cnt-1) ); return 0;
	}
	else if ( (s[1]=='0') && (s[2]=='1') && (s[3]=='1') )
	{
		for ( int i=3; i<=n+2; i++ )
			if ( str[i]!=s[i-2] ) { printf( "0\n" ); return 0; }
		cnt=((n+2)%3) ? (n+2)/3+1 : (n+2)/3;
		printf( "%lld\n",10000000000-(cnt-1) ); return 0;
	}
	else { printf( "0\n" ); return 0; }
}

C - Exoswap

给定一个长为 \(N\) 的排列 \(P=P_1,P_2,\dots ,P_N\) .

你需要以任意顺序做以下 \(N-1\) 个操作,每个 恰好一次

  • 交换 \(P_i,P_{i+1}(i=1\sim n-1)\) .

问是否能将 \(P\) 升序排列,如果不能输出 -1 ,如果能给出方案。

Thoughts & Solution

考场的奇怪思路 AC 了……

这题不建议看这篇题解,是考场乱搞的做法(不过做法是对的,就是代码思路比较奇怪)

  • 交换两个相邻的元素,逆序对增减量为 1 。那么如果总逆序对个数多于 \(n\) 个,显然 Impossible.
  • 如果有三个连续元素递减,那么显然 Impossible.
  • 对于所有连续逆序对 \(P_i>P_{i+1}\) ,将 \(i\) 放入队列中,依次处理:
    • 每次取出队首,如果仍然为逆序对,那么交换,\(cnt++\)
    • 判断前一个和后一个,如果出现逆序对,入队;
  • 最后判断是否恰好交换 \(n-1\) 次,和是否所有元素都归位。
//Author: RingweEH
#define lowbit(x) ((x)&(-x))
#define PII pair<int,int>
const int N=2e5+10;
int n,p[N],c[N],ans[N];
queue<int> q;

void add( int x,int val ) { for ( ; x<=n; x+=lowbit(x) ) c[x]++; }
int get_sum( int x ) { int res=0; for ( ; x; x-=lowbit(x) ) res+=c[x]; return res; }

int main()
{
	n=read(); 
	for ( int i=1; i<=n; i++ )
		p[i]=read();
	
	int cnt=0;
	for ( int i=n; i>=1; i-- )
	{
		cnt+=get_sum( p[i]-1 );
		if ( cnt>n ) { printf( "-1" ); return 0; }
		add( p[i],1 );
	}
	if ( cnt>n ) { printf( "-1" ); return 0; }
	for ( int i=1; i<n-1; i++ )
		if ( (p[i]>p[i+1]) && (p[i+1]>p[i+2]) ) { printf( "-1" ); return 0; }
	for ( int i=1; i<n; i++ )
		if ( (p[i]>p[i+1]) ) q.push( i );
	cnt=0;
	while ( !q.empty() )
	{
		int u=q.front(); q.pop();
		if ( p[u]>p[u+1] ) swap( p[u],p[u+1] ),ans[++cnt]=u;
		if ( (u>1) && (p[u-1]>p[u]) ) q.push( u-1 );
		if ( (u<n-1) && (p[u+1]>p[u+2]) ) q.push( u+1 );
	}
	
	if ( cnt!=(n-1) ) { printf( "-1" ); return 0; }
	for ( int i=1; i<=n; i++ )
		if ( p[i]!=i ) { printf( "-1" ); return 0; }
	for ( int i=1; i<n; i++ )
		printf( "%d\n",ans[i] );
}

D - Binomial Coefficient is Fun

给定一个长为 \(n\) 的非负整数序列 \(A\)

对于所有满足 \(\sum_i B_i\leq M,B_i\in N\) 的序列 \(B\) ,求 $$\prod_{i=1}^n {B_i\choose A_i}$$ 的和对 \(1e9+7\) 取模的结果。

\(1\leq N\leq 2000,1\leq M\leq 1e9,0\leq A_i\leq 2000\) .

Thoughts & Solution

常见套路:有子问题 \(\sum B_i=M\) .

由于所求是乘积,那么就只有 \(B_i\ge A_i\) 时才会有贡献。

考虑这个问题的组合意义,将 \(B\) 看做 \(M\) 个小球。由于 \(B_i\) 的值可以改变,考虑加入 \(N-1\) 个隔板来分隔 \(B_i,B_{i+1}\) .

然后来放置 \(A\) 。灵活运用 \(B\) 可变这一性质,可以通过 \(A\) 来确定隔板位置,即:

  • 总共选出 \(\sum A_i+N-1\) 个位置;
  • \(1\sim A_1\) 表示 \(A_1\)\(A_1+1\) 是第一个隔板,后面以此类推。
  • \(B_i\) 的值随着隔板位置变化,且一定合法。

问题转化为 \(M+N-1\) 个位置中选择 \(\sum A_i+N-1\) 个,子问题答案为:

\[ans_{sub}={ M+N-1\choose\sum A_i+N-1} \]

然后来看最终答案。

\[ans=\sum_{i=\sum A_j}^M {i+N-1\choose \sum A_j+N-1}\\ =\sum_{i=\sum A_j+N-1}^{M+N-1}{i\choose \sum A_j+N-1}\\ \because \sum_{i=k}^n{i\choose k}={n+1\choose k+1 }\\ \therefore ans={M+N\choose \sum A_j+N} \]

//Author: RingweEH
const ll Mod=1e9+7;
ll n,m;

ll power( ll a,ll b )
{
    ll res=1;
    for ( ; b; b>>=1,a=a*a%Mod )
        if ( b&1 ) res=res*a%Mod;
    return res;
}

int main()
{
    n=read(); m=read(); int sum=0;
    for ( int i=1; i<=n; i++ )
        sum=sum+read();
    
    if ( m<sum ) { printf( "0\n" ); return 0; }
    m+=n; sum+=n;
    ll ans=1;
    for ( ll i=1; i<=sum; i++ )
        ans=ans*power( i,Mod-2 )%Mod*(m-i+1)%Mod;

    printf( "%lld\n",ans );
}

E - Shorten ABC

给定一个只包含 ABC 的字符串 \(s\) ,每次可以选择相邻的两个不同的字符,合成与他们不同的字符。

问任意次操作(可以为 0 )后能变成多少不同的字符串,对 \(1e9+7\) 取模。\(|S|\leq 1e6\) .

Thoughts & Solution

A ,B, C 分别为 \(1,2,3\) ,如果不考虑相邻的不相同的限制,那么这样的“合成”就是将 \(x,y\) 替换为 \(x\oplus y\) .

对于 \(x\neq y\) 的限制,归纳可证,等价于区间异或和不为 \(0\) 且区间内字符不完全相等或区间长度为 \(1\) .

这样,题目的要求就是,将原序列划分成若干个区间求异或值。

为了保证方案不算重,强制除了第一个区间,任意区间的任意非空前缀异或和不为 \(0\) ,否则可以将该前缀加入上一个区间。

\(f[i]\) 表示划分前 \(i\) 个的方案数。转移方程就是:

\(nxt[i]\) 表示 \(i+1\sim n\) 中最早出现的 \(j\) ,使得 \(suma[i]=suma[j]\) ,其中 \(suma\) 表示异或前缀和。

\[f[i]=>f[i+1],\dots,f[nxt[i]-1] \]

这部分可以用差分维护。

//Author: RingweEH
const int N=1e6+10;
const ll Mod=1e9+7;
int n,a[N],las[5],nxt[N];
ll f[N];
char s[N];

void add( int l,int r,ll val )
{
	f[l]=(f[l]+val)%Mod;
	if ( r+1<=n ) f[r+1]=(f[r+1]+Mod-val)%Mod;
}

int main()
{
	n=read(); scanf( "%s",s+1 );
	
	a[0]=0; bool fl=0;
	for ( int i=1; i<=n; i++ )
	{
		if ( s[i]=='A' ) a[i]=1;
		else if ( s[i]=='B' ) a[i]=2;
		else a[i]=3;
		if ( (a[i]^a[i-1]) && (i>1) ) fl=1;
	}
	if ( !fl ) { printf( "1\n" ); return 0; }		//判断全部相等
	for ( int i=1; i<=n; i++ )
		a[i]^=a[i-1];
	for ( int i=0; i<5; i++ )
		las[i]=n+1;
	for ( int i=n; i>=1; i-- )
		nxt[i]=las[a[i]],las[a[i]]=i;
	for ( int i=1; i<=n; i++ )
		if ( a[i] ) add( i,i,1 );
	for ( int i=1; i<=n; i++ )
	{
		if ( i>1 ) f[i]=(f[i]+f[i-1])%Mod;
		add( i+1,nxt[i]-1,f[i] );
	}

	printf( "%lld\n",f[n] );

	return 0;
}

F - Esoswap

给定一个长为 \(N(2\leq N\leq 100)\) 的排列 \(P=P_0,\dots,P_N-1\) .你可以进行以下操作最多 \(2e5\) 次:

  • 选择一个数 \(i(0\leq i\leq N-1)\) ,交换 \(P_i,P(i+P_i)\bmod N\)

如果最终不能成为升序,输出 -1 ,否则给出方案。

Thoughts & Solution

先来观察一下样例。

8
7 1 2 6 4 0 5 3

发现有一个很简单的操作方式:无脑操作 \(0\) 这个位置。

7 1 2 6 4 0 5 3
3 1 2 6 4 0 5 7
6 1 2 3 4 0 5 7
5 1 2 3 4 0 6 7
0 1 2 3 4 5 6 7

考虑对 \(0\) 这个位置,有:

  • 每个在这里的数都会被归位到 \(P_i\) 即最终位置
  • 这样的变换不会重复(显然,因为每次到这里的 \(P\) 不可能一样)
  • 变换到 \(0\) 归位之后终止

进一步的,发现对于所有位置,除了第一条以外都成立。

然后,考虑进行如下构造:

  • \(n-1\sim 1\) 倒序操作使得 \(p[i]=n-1-i\)

    Proof

    当你操作 \(i\) 位置时,已经固定了 \(i+1\sim n-1\)\([0,n-i-1]\) 这些数都已经固定下来,因此操作不会破坏已固定的数的顺序。

    由于上面的性质,变换不会重复,因此这部分操作数至多 \(n^2\) .

  • 现在所有数都已经降序排列了。考虑通过 \(1\) 来调换。

  • 设已经排成了 “倒序+\(0\ 1\) +顺序”的形式。那么,我们可以通过移动 \(1\) 到末尾,然后再和前面的调换插入下一个数。

  • Example:

    7 6 5 4 0 1 2 3 => 7 6 5 4 0 2 1 3 => 7 6 5 4 0 2 3 1 (调换1)
    7 6 5 1 0 2 3 4 => 7 6 5 0 1 2 3 4 (插入)
    

然后就做完了。所以无解是啥啊

//Author: RingweEH
const int N=110;
int n,p[N];
vector<int> ans;

void Operation( int x )
{
	ans.push_back( x );
	swap( p[x],p[(x+p[x])%n] );
}

int main()
{
	n=read();
	for ( int i=0; i<n; i++ )
		p[i]=read();
	
	for ( int i=n-1; i; i-- )
		while ( p[i]^(n-i-1) ) Operation( i );
	for ( int i=n-2; i>=0; i-- )
	{
		for ( int j=i+1; j<n-1; j++ )
			Operation( j );
		Operation( i ); Operation( i );
	}

	printf( "%d\n",ans.size() );
	for ( int i=0 ;i<ans.size(); i++ )
		printf( "%d\n",ans[i] );

    return 0;
}
posted @ 2020-12-16 17:04  MontesquieuE  阅读(304)  评论(2编辑  收藏  举报