题解 |文远知行杯广东工业大学第十六届程序设计竞赛

有点意思的一场。可惜只会签到。

本题解包含ABCEFHI。

A

经典分块。先预处理a[1]到a[1e4]。对L到R中的某个值,比如k,如果k小于1e4就直接用处理好的答案。k大于1e4单独讨论。 注意到i=n/z+1,z>=2时,n%i为部分最大值。如:n=100时,z=2,i=n/z+1=51(此时n%i=49),z=3,i=n/z+1=34(此时n%i=32)等等。预处理出z=2到z=1e4的i值,逐一检查是否在L到R内即可。 复杂度O(sqrt(n)m)

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

#define mod 1000000007
#define debug 1
#define deb(x) if(debug) cout<<#x<<"="<<x<< endl;

#define for1(i,a,b) for(i=(a);i<=(b);i++)
#define for2(i,a,b) for(i=(a);i>=(b);i--)

ll p[1000005],q[1000005],s[1000005];

int main()
{
	ll t,n,m,i,j,k,l,r;
	
	cin>>n>>t;
	
	for(i=1;i<=1e4;i++)
	{
		p[i]=n%i;
	}
	
	for(i=1;i<=1e4;i++)
	{
		q[i]=n/i+1;
		s[i]=n%q[i];
	}
	
	while(t--)
	{
		ll ans=0;
		scanf("%lld %lld",&l,&r);
		
		ans=max(n%l,n%r);
		for(i=l;i<=min((ll)10000,r);i++) ans=max(ans,p[i]);
		
		for(i=1;i<=min((ll)10000,n);i++)
		{
			if(l<=q[i]&&q[i]<=r)
			ans=max(ans,s[i]);
		}
		
		printf("%lld\n",ans);
	}

	return 0;
}

B

签到。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

#define mod 1000000007
#define debug 1
#define deb(x) if(debug) cout<<#x<<"="<<x<< endl;

#define for1(i,a,b) for(i=(a);i<=(b);i++)
#define for2(i,a,b) for(i=(a);i>=(b);i--)

char p[1000005],q[1000005];

int main()
{
	ll t,n,m,i,j,k;
	
	
	scanf("%s",&p);
	p[48]='0';
	
	ll ok=0;
	for(i=0;i<48;i++)
	{
		j=i;
		if(p[j]=='0') continue;
		
		while(p[j]=='1') j++;
		printf("%02lld:%02d - %02lld:%02d\n",
		i/2,i%2?30:0,j/2,j%2?30:0
		);
		
		ok=1;
		
		
		i=j;
	}
	if(!ok) printf("00:00 - 00:00");
	
	
	

	return 0;
}

C

先注意到显然如果对某行i,某行j,某列k,有p[i][k+1]-p[i][k]!=p[j][k+1]-p[j][k],那么无解。注意这里的减法是模意义下的。

然后,我们先随便找出一个解。这个解写成如下形式:对每行,加上a1,a2,a3...an。对每列,加上b1,b2,b3...bm。

我们可以给a1-an所有数+1,然后给b1-bm所有数-1。注意这里的加减法全部都是模意义下的。显然这样的一步操作不会改变方格的结果。贪心的想一下就能发现,不断的执行这个操作,当a1-an和b1-bm这n+m个数中有一个数为0时,它可能是最优的情况。所以我们遍历一遍每个数,试着计算让它成为0时这n+m个数的和,最终取最小即可。

这题卡了快读。意义不明。

#include<bits/stdc++.h>

using namespace std;

#define int long long
typedef int ll;

#define mod 1000000007
#define debug 1
#define deb(x) if(debug) cout<<#x<<"="<<x<< endl;

#define for1(i,a,b) for(i=(a);i<=(b);i++)
#define for2(i,a,b) for(i=(a);i>=(b);i--)

ll p[2005][2005],q[1000005];

ll s1[2005],s2[2005],n1=0,n2=0;


long long calc(ll type,ll val,ll k)
{
	if(val==k) val=0;
	long long ret=0;
	int i,now;
	for(i=1;i<=n1;i++)
	{
		if(type==1) now=s1[i]+val+k;
		else now=s1[i]-val+k;
		
		while(now>=k) now-=k;
		ret+=now;
	}
	for(i=1;i<=n2;i++)
	{
		if(type==1) now=s2[i]-val+k;
		else now=s2[i]+val+k;
		
		while(now>=k) now-=k;
		ret+=now;
	}
//	cout<<val<<" "<<ret<<endl;
	return ret;
}

//------------------------------------

inline int read()
{
	int x=0,y=1;char c=getchar();//y代表正负(1.-1),最后乘上x就可以了。
	while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}//如果c是负号就把y赋为-1
	while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*y;//乘起来输出
}

//------------------------------------



signed main()
{
//	freopen("in.txt","r",stdin); 
	
	ll t,n,m,i,j,k;
	
	cin>>n>>m>>k;
	
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			p[i][j]=read();
		}
	}
	
	for(i=1;i<=n;i++)
	{
		ll need=k-p[i][1];
		if(need>=k) need-=k;
		for(j=1;j<=m;j++)
		{
			p[i][j]=p[i][j]+need;
			if(p[i][j]>=k) p[i][j]-=k;
		}
		s1[++n1]=need;
	}
	
	for(j=1;j<=m;j++)
	{
		ll need=k-p[1][j];
		if(need>=k) need-=k;
		for(i=1;i<=n;i++)
		{
			p[i][j]=p[i][j]+need;
			if(p[i][j]>=k) p[i][j]-=k;
		}
		s2[++n2]=need;
	}
	
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			if(p[i][j])
			{
				printf("-1");
				return 0;
			}
		}
	}
	
	
	long long ans=1e17;
	
	for(i=1;i<=n;i++) ans=min(ans,calc(1,k-s1[i],k));
	
	for(i=1;i<=m;i++) ans=min(ans,calc(2,k-s2[i],k));
	
	printf("%lld",ans);
	
	return 0;
}
/*
3 3 10
0 0 9 
0 0 9 
9 9 8

3 3 10
8 9 9 
9 0 0 
9 0 0

3 3 10
7 7 9
7 7 9
7 7 9

*/

D

不会。

E

写一个int solve(L,R)函数表示如果只考虑L到R,此时从L层开始最多可以爬多少层。先预处理所有solve(i,n),其中1<=i<=n,记在q[i]中。 对单次询问L,R,我们可以直接起点为L到起点为R-10的答案。因为这些地方的终点肯定不会超过R。然后暴力解决剩下的小部分。

#include<bits/stdc++.h>

using namespace std;

typedef int ll;

#define mod 1000000007
#define debug 1
#define deb(x) if(debug) cout<<#x<<"="<<x<< endl;

#define for1(i,a,b) for(i=(a);i<=(b);i++)
#define for2(i,a,b) for(i=(a);i>=(b);i--)

char p[100005];
ll q[100005];


//---------------------------------------------
//---------------------------------------------
//---------------------------------------------

const int maxn=100005;
int h[maxn],a[maxn];
int n,m;
int lowbit(int x)
{
    return x&(-x);
}
void update(int x)
{
    while(x<=n)
    {
        h[x]=a[x];
        for(int i=1;i<lowbit(x);i<<=1)
        h[x]=max(h[x],h[x-i]);
        x+=lowbit(x);
    }
    return ;
}
int findans(int begin,int end)
{
    int ans=0;
    while(end>=begin)
    {
        ans=max(ans,a[end]);
        end--;
        for(;end-lowbit(end)>=begin;end-=lowbit(end))
        ans=max(ans,h[end]);
    }

    return ans;
}

//---------------------------------------------
//---------------------------------------------
//---------------------------------------------


ll solve(ll st,ll ed)
{
	ll ret=0,i,hp=1;
	for(i=st;i<=ed;i++)
	{
		if(p[i]=='0') hp--;
		else hp++;
		if(hp==1) ret=max(ret,i-st+1);
		if(hp<=0) break;
		
		if(i-st>10) break;
	}
	
	return ret;
}

//------------------------------------

inline ll read()
{
	ll x=0,y=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
	while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x;
}

//------------------------------------

int main()
{
	ll t,i,j,k;
	
	cin>>n;
	scanf("%s",p+1);
	
	for(i=1;i<=n;i++) q[i]=solve(i,n);
	
	for(i=0;i<=n+2;i++) h[i]=0;
	for(i=1;i<=n;i++) a[i]=q[i],update(i);
	
//	for(i=1;i<=n;i++) printf("%lld ",q[i]);
	
	ll L,R;
	t=read();
	while(t--)
	{
		L=read();R=read();
		
		ll ans=0;
		if(R-11>=L) ans=findans(L,R-11);
		
		for(i=max(L,R-12);i<=R;i++)
		{
			ans=max(ans,solve(i,R));
		}
		
		printf("%d\n",ans);
	}
	
	
	

	return 0;
}
/*
44
10101010101010101010111000111000000111000111

*/

F

想象两个数一左一右,一开始都是1。对每个质数如质数p,它若有k个,则它可以让左侧乘上x次p,其中1<=x<=k。它也可以让右侧乘上x次p,它也可以两边都不乘,但不可以两边都乘(否则这两个数就不互质了)。因此它有2k+1种选择。把总选择数相乘。


#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

#define mod 1000000007
#define debug 1
#define deb(x) if(debug) cout<<#x<<"="<<x<< endl;

#define for1(i,a,b) for(i=(a);i<=(b);i++)
#define for2(i,a,b) for(i=(a);i>=(b);i--)

ll p[1000005],q[1000005];

int main()
{
	ll t,n,m,i,j,k;
	cin>>t;
	while(t--)
	{
		ll ans=1;
		scanf("%lld",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%lld %lld",&j,&k);
			ans=(ans*(k*2+1))%mod;
		}
		
		printf("%lld\n",ans);
	}
	return 0;
}

G

不会。

H

对一个串,要将它全变0或全变1,最少操作次数是这个串中相邻两位不相等的个数。如000111001,至少要3次。因为它形如000-111-00-1,共有三个地方是相邻两位不相等的。 因此我们维护相邻两位不相等的个数。这可以用线段树实现。


#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

#define mod 1000000007
#define debug 1
#define deb(x) if(debug) cout<<#x<<"="<<x<< endl;

#define for1(i,a,b) for(i=(a);i<=(b);i++)
#define for2(i,a,b) for(i=(a);i>=(b);i--)

//------------------------------------------------------------
//------------------------------------------------------------
//------------------------------------------------------------
#define MAXN 1000005
ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
inline ll ls(ll x)
{
    return x<<1;
}
inline ll rs(ll x)
{
    return x<<1|1;
}
inline void push_up(ll p)
{
    ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r)
{
    tag[p]=0;
    if(l==r){ans[p]=a[l];return ;}
    ll mid=(l+r)>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    push_up(p);
} 
inline void f(ll p,ll l,ll r,ll k)
{
    tag[p]=tag[p]+k;
    ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(ll p,ll l,ll r)
{
    ll mid=(l+r)>>1;
    f(ls(p),l,mid,tag[p]);
    f(rs(p),mid+1,r,tag[p]);
    tag[p]=0;
}
inline void update(ll nl,ll nr,ll l,ll r,ll p,ll k)
{
    if(nl<=l&&r<=nr)
    {
        ans[p]+=k*(r-l+1);
        tag[p]+=k;
        return ;
    }
    push_down(p,l,r);
    ll mid=(l+r)>>1;
    if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
    push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p)
{
    ll res=0;
    if(q_x<=l&&r<=q_y)return ans[p];
    ll mid=(l+r)>>1;
    push_down(p,l,r);
    if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
    if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));
    return res;
}
//------------------------------------------------------------
//------------------------------------------------------------
//------------------------------------------------------------
 
char p[1000005];
ll now[1000005]={0};

int main()
{
	ll t,i,j,k;
	
	cin>>n>>t;
	
	build(1,1,n);
	
	scanf("%s",p+1);
	for(i=2;i<=n;i++)
	{
		if(p[i]!=p[i-1])
		{
			update(i,i,1,n,1,1);
			now[i]=1;
		}
	}
	
	ll type,L,R;
	while(t--)
	{
		scanf("%lld %lld %lld",&type,&L,&R);
		if(type==2)
		{
			if(L!=1)
			{
				if(now[L]==0)
				{
					update(L,L,1,n,1,1);
				}
				else
				{
					update(L,L,1,n,1,-1);
				}
				now[L]=!now[L];
			}
			R++;
			if(R!=n+1)
			{
				if(now[R]==0)
				{
					update(R,R,1,n,1,1);
				}
				else
				{
					update(R,R,1,n,1,-1);
				}
				now[R]=!now[R];
			}
		}
		else if(type==1)
		{
			printf("%lld\n",query(L+1,R,1,n,1));
		}
	}

	return 0;
}
/*
10 5
0010010010
0010001101
*/

I

对任何i,对位置在i,i+k,i+2k...的物品,我们称他们是一组的。显然不同组的物品不会互相影响,因为勾爪永远不可能同时抓起不在同一组内的物品。 对每组单独考虑。问题变成了一个勾爪每次必须抓起相邻的两个物品。可以用dp实现。


#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

#define mod 1000000007
#define debug 1
#define deb(x) if(debug) cout<<#x<<"="<<x<< endl;

#define for1(i,a,b) for(i=(a);i<=(b);i++)
#define for2(i,a,b) for(i=(a);i>=(b);i--)

ll p[1000005],q[1000005];
ll dp[1000005][2];

vector<ll> x;

ll solve()
{
	ll ret=0;
	ll n=x.size();
	
	ll i,j;
	for(i=0;i<=n+3;i++) dp[i][0]=0,dp[i][1]=0;
	
	for(i=0;i<n-1;i++)
	{
		dp[i+1][1]=max(dp[i+1][1],dp[i][0]);
		
		dp[i+2][1]=max(dp[i+2][1],dp[i][1]+x[i]+x[i+1]);
		dp[i+1][1]=max(dp[i+1][1],dp[i][1]);
	}
	
	for(i=0;i<=n;i++)
	{
		for(j=0;j<=1;j++)
		{
//			cout<<dp[i][j]<<" ";
			ret=max(ret,dp[i][j]);
		}
	}
	return ret;
}

int main()
{
	ll t,n,m,i,j,k;
	
	cin>>n>>k;
	
	for(i=1;i<=n;i++) scanf("%lld",&p[i]);
	
	
	ll ans=0;
	
	for(i=1;i<=k;i++)
	{
		x.clear();
		for(j=i;j<=n;j+=k)
		{
			x.push_back(p[j]);
		}
		ans+=solve();
	}
	
	cout<<ans;

	return 0;
}

J

不会。

会的构造和贪心乱杀,不会的还是到死都做不出来,咋办啊。烂完了。

全部评论

相关推荐

totoroyyw:千年老妖😂
投递华为等公司10个岗位
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务