首页 > 其他 > 详细

Codeforces#689 Div.2

时间:2020-12-13 20:39:19      阅读:25      评论:0      收藏:0      [点我收藏+]

Contest Link Official Editorial

A - String Generation

构造一个只包含 abc 的长度为 \(n\) 的字符串,使得最长回文子串长度不超过 \(k\) .

Thoughts & Solution

赛时看错了,以为是最长回文子串长度为 \(k\) ,还恰好构造了一个(虽然这样也能过)……后来发现是不超过,那没事了。

//Author: RingweEH
int main()
{
	int T=read();
	while ( T-- )
	{
		int n=read(),k=read();
		for ( int i=1; i<=k; i++ )
			printf( "a" );
		char ch=‘b‘;
		for ( int i=1; i<=(n-k); i++ )
		{
			printf( "%c",ch ); ch=ch+1;
			if ( ch==‘d‘ ) ch=‘a‘;
		}
		printf( "\n" );
	}
}

B - Find the Spruce

给定一个只包含 *. 的矩阵,求其中有多少 * 组成的 spruce. \(n,m\leq 500\) .

标准长这样(反正就是每一层都比上一层两边多一个,至于多少层并不关心,也就是一个 \(k\) 层能被算 \(k\) 次):

技术分享图片

Thoughts & Solution

预处理出 \(sum[i][j]\) 表示从第 \(i\) 行第 \(j\) 个格子开始,能往右拓展多少个为 * 的格子。

直接暴力枚举最上面一个,然后暴力往下拓展,\(\mathcal{O}(1)\) 算这一行往右的格子够不够(然后判掉越界的)。

复杂度讲道理应该是 \(\mathcal{O}(n^2m)\) 的,但是跑不满。(注意预处理 \(sum\) 的写法并不是 \(\mathcal{O}(nm^2)\) 的,后面的循环也显然不可能到这个上界)

//Author: RingweEH
const int N=510;
int n,m,sum[N][N],a[N][N];
char s[N][N];

int main()
{
	int T=read();
	while ( T-- )
	{
		n=read(); m=read();
		for ( int i=1; i<=n; i++ )
		{
			scanf( "%s",s[i] );
			for ( int j=1; j<=m; j++ )
				a[i][j]=(s[i][j-1]==‘*‘);
			sum[i][0]=0;
			for ( int j=1; j<=m; j++ )
			{
				if ( sum[i][j-1]>0 ) { sum[i][j]=sum[i][j-1]-1; continue; }
				sum[i][j]=0;
				for ( int k=j; k<=m; k++ )
					if ( a[i][k] ) sum[i][j]++;
					else break;
			}
		}
		
		ll ans=0;
		for ( int i=1; i<=n; i++ )
			for ( int j=1; j<=m; j++ )
			{
				int cnt=1,now=1;
				while ( 1 )
				{
					int l=j-now/2,r=j+now/2;
					//printf( "i=%d j=%d l=%d r=%d sum=%d\n",i,j,l,r,sum[i][l] );
					if ( l<=0 || r>m || (i+cnt-1)>n ) break;
					if ( sum[i+cnt-1][l]<now ) break;
					cnt++; now+=2;
				}
				cnt--; ans+=cnt;
			}
		
		printf( "%lld\n",ans );
	}
	
	return 0;
}

C - Random Events

给定一个长度为 \(n\) 的排列 \(a\) ,给出 \(m\) 次操作,每次操作如下:

  • 给出一个数 \(r_i\) 和一个概率 \(p_i\)
  • 对于排列 \(a\) ,以 \(p_i\) 的概率对 \([1,r_i]\) 升序排序

求做完之后序列升序的概率。

Thoughts & Solution

思路其实很简单。但是有个地方让我一直 Wa on test2 ……就离谱。

设最开始 \([las,n]\) 的数字都是已经就位的。那么只要任意一个 \(r_i\ge las-1\) 的操作被执行了,最后就一定是有序的,反之则一定不行。直接计算出这样的 \((1-r_i)\) 的乘积,最后用 1 减去即可,特判一开始就有序的情况。

但是……这道题是多测!我当时在读入了 \(a\) 数组之后就直接特判 并且 continue 了,完全没想到后面没读入完会造成下一个 Case 原地爆炸,而题面样例中升序的又恰好是最后一个 Case ……我当场暴毙。

//Author: RingweEH
const int N=1e5+10;
struct node
{
	int pos; double pro;
}b[N];
int n,m,a[N];

int main()
{
	int T=read();
	while ( T-- )
	{
		n=read(); m=read();
		for ( int i=1; i<=n; i++ )
			a[i]=read();
		for ( int i=1; i<=m; i++ )
			b[i].pos=read(),scanf( "%lf",&b[i].pro );
		
		int las=0;
		for ( int i=n; i>=1; i-- )
			if ( a[i]!=i ) { las=i+1; break; }
		double ans=1.0;
		for ( int i=1; i<=m; i++ )
			if ( b[i].pos>=(las-1) ) ans=ans*(1.0-b[i].pro);
		
		if ( las==0 ) { printf( "1.000000\n" ); continue; }
		printf( "%.6lf\n",1.0-ans );
	}
}

D - Divide and Summarize

给定一个长为 \(n\) 的序列 \(a\)\(m\) 个询问 \(s_i\) .你可以进行若干次操作,每次如下:

  • 设序列中 \(\max +\min=mid\)
  • 将序列中所有 \(\leq mid\) 的数放入 \(a_1\) ,其余放入 \(a_2\)
  • 选择其中一个数组保留,另一个丢弃

每次询问,问经过若干次操作,是否能使序列和为 \(s_i\) .

Thoughts & Solution

显然序列的顺序没有任何关系,所以可以在开始的时候先排序。然后预处理出前缀和 \(sum\)\(pos[i]\) ,表示 \(\leq i\) 的数列的 \(endpos\) .

然后就可以直接模拟了。每次类似分治地递归 \(mid\) 两边即可,由于顺序不会改变,所以复杂度是 \(\mathcal{O}(\log n)\) 的。将过程中产生的所有结果放到一个 unordered_set 里面,对于每个询问直接 count 即可。

我还是第一次知道 Codeforces 交文件居然不能带中文注释的……

//Author: RingweEH
const int N=1e6+10;
int n,m,a[N],pos[N];
ll sum[N];
unordered_set<ll> s;

void solve( int l,int r )
{
	s.insert( sum[r]-sum[l-1] );
	if ( l>=r ) return;
	int mid=(a[l]+a[r])>>1;
	if ( pos[mid]+1>r ) return;
	solve( l,pos[mid] ); solve( pos[mid]+1,r );
}

int main()
{
	int T=read();
	while ( T-- )
	{
		n=read(); m=read();
		for ( int i=1; i<=n; i++ )
			a[i]=read();
		
		sort( a+1,a+1+n ); sum[0]=0;
		for ( int i=1; i<=n; i++ )
			sum[i]=sum[i-1]+a[i];
		s.clear();
		for ( int i=a[1]; i<=a[n]; i++ )
			pos[i]=0;
		for ( int i=1; i<=n; i++ )
			pos[a[i]]=i;
		for ( int i=a[1]; i<=a[n]; i++ )
			if ( !pos[i] ) pos[i]=pos[i-1];
		solve( 1,n );
		while ( m-- )
			puts( s.count(read()) ? "Yes" : "No" );
	}
}

E - Water Level

有一个水池,初始有 \(k\) 的水位,现在希望将每天的水位控制在 \([l,r]\) 之间。每一天:

  • 会用掉 \(x\) 的水
  • 在一天的开始可以选择加入 \(y\) 的水

问能否在 \(t(t\leq 1e18)\) 天内保持这个水位。

Thoughts & Solution

如果有 \(y\leq x\) ,那么显然水会不断减少,只需要计算原水位是否合法,以及是否够 \(t\) 天消耗即可。

现在来考虑不满足的情况。

显然,每一次会尽可能让水减到最少,然后再增加 \(y\) .但是如果直接模拟的话,这个数据范围显然会炸。那就找循环节。如果某一次被减少到了 \(k\bmod x\) ,那么后面的东西都进入了循环,这个过程一定可以重复下去。

看上去很简单的思路对吧。但是实现上细节很多:

  • 要注意,增加 \(y\) 一定是在减少之前加的,要在减完判断完之后立即预判是否要加
  • 如果不能加不能减了就判负
  • \(x\ge y\) 的情况里也有很多:
    • 一开始要判断 \(k+y\) 是否大于 \(r\) ,如果大于就减去一个 \(x\)
    • 做完上面那条要判是否 \(k<l\) ,其实就是不能加不能减
    • 如果经过减去 \(x\) 之后 \(t\) 为 0 了要判掉,但是不能放在第二条上面(否则 \(k\) 越界就没了)
    • 最后判是否 \(t\) 次够减的时候要除法不要乘法,会炸 long long .
//Author: RingweEH
ll k,l,r,t,x,y;
bool vis[1000010];

int main()
{	
	k=read(); l=read(); r=read(); t=read(); x=read(); y=read();
	if ( k<l || k>r ) { printf( "No\n" ); return 0; }
	if ( x>=y )
	{
		if ( k+y>r ) k-=x,t--;
		else k+=y;
		if ( k<l ) { printf( "No\n" ); return 0; }
		if ( !t ) { printf( "Yes\n" ); return 0; }
		if ( (x-y)<=(k-l)/t ) printf( "Yes\n" );
		else printf( "No\n" );
		return 0;
	}
	k-=l; r-=l; ll sum=0;
	if ( k+y<=r ) k+=y;
	while ( 1 )
	{
		if ( vis[k%x] ) { printf( "Yes\n" ); return 0; }
		vis[k%x]=1; ll cnt=k/x; sum+=cnt; k-=cnt*x;
		if ( sum>=t ) { printf( "Yes\n" ); return 0; }
		cnt=(r-k)/y;
		if ( cnt==0 ) { printf( "No\n" ); return 0; }
		k+=cnt*y;
	}
	return 0;
}

F - Mathematical Expression

给定 \(n-1\) 个数和若干允许使用的运算符(保证只有 +-* ),在其中插入 \(n-1\) 个使得最后的结果最大。

Thoughts & Solution

我先胡一个大型分讨(

  • 如果是一个运算符,全写上。
  • 如果是两个:
    • +* ,0/1 两边是 + ,其余是 *
    • +- ,全 +
    • -* ,如果有 0 的话找到第一个 0 ,在其前面是 - ,其他全部是 * ;如果没有,那么全 *
  • 如果是三个:
    • 减法应该是没有用的。
    • 所有 0/1 左右都是 + ,其余都是 *

不知道对不对……

好极了,被 Hack 了。数据如下( WA on test 9

Input
10
2 1 1 2 1 2 1 2 1 2
+*
Answer
2*1*1*2*1*2*1*2*1*2

其实就是,在 +*+-* 的地方出了点问题,改成 DP 就好了。

//Author: RingweEH
const int N=1e5+10;
const ll inf=1e10;
int n,a[N],num[N],tot,pre[N];
ll sum[N],f[N];
bool opt[N],add,del,tim;
char s[5];

ll minn( ll x )
{
    return min( x,inf );
}

ll calc( int i,int j )
{
    return sum[num[j]]/sum[num[i]]+f[i]+num[i+1]-num[i]-1;
}

void solve( int l,int r )
{
    if ( l>r ) return;
    sum[l-1]=1;
    for ( int i=l; i<=r; i++ )
        sum[i]=minn( a[i]*sum[i-1] );
    if ( sum[r]>=inf )
    {
        for ( int i=l; i<=r && a[i]==1; i++ )
            opt[i+1]=1;
        for ( int i=r; i>=l && a[i]==1; i-- )
            opt[i]=1;
        return;
    }
    tot=0;
    for ( int i=l; i<=r; i++ )
        if ( a[i]>1 ) num[++tot]=i;
    for ( int i=l; i<=r; i++ )
        opt[i]=1;
    num[0]=l-1; f[0]=0;
    for ( int i=1; i<=tot; i++ )
    {
        int mi=0;
        for ( int j=1; j<i; j++ )
            if ( calc( mi,i )<calc( j,i ) ) mi=j;
        f[i]=calc( mi,i ); pre[i]=mi;
    }
    for ( int i=tot; i; i=pre[i] )
        for ( int j=num[i]; j>num[pre[i]+1]; j-- ) 
            opt[j]=0;
}

int main()
{
    n=read();
    for ( int i=1; i<=n; i++ )
        a[i]=read();
    scanf( "%s",s );

    int len=strlen( s );
    if ( len==1 )
    {
        printf( "%d",a[1] );
        for ( int i=2; i<=n; i++ )
            printf( "%c%d",s[0],a[i] );
        return 0;
    }

    for ( int i=0; i<len; i++ )
        if ( s[i]==‘+‘ ) add=1;
        else if ( s[i]==‘-‘ ) del=1;
        else tim=1;
    if ( !add )
    {
        printf( "%d",a[1] );
        for ( int i=2; i<=n; i++ )
            printf( "%c%d",(a[i]==0 ? ‘-‘ : ‘*‘),a[i] );
        return 0;
    }
    if ( !tim )
    {
        printf( "%d",a[1] );
        for ( int i=2; i<=n; i++ )
            printf( "%c%d",‘+‘,a[i] );
        return 0;
    }
    //-----------------------
    opt[1]=1;
    for ( int i=1,las=0; i<=n+1; i++ )
        if ( a[i]==0 )
        {
            opt[i]=opt[i+1]=1;
            solve( las+1,i-1 ); las=i;
        }
    //---------------------
    printf( "%d",a[1] );
    for ( int i=2; i<=n; i++ )
        printf( "%c%d",(opt[i]==0 ? ‘*‘ : ‘+‘),a[i] );
}

Codeforces#689 Div.2

原文:https://www.cnblogs.com/UntitledCpp/p/14128888.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!