北京Day 17

今天开始颓了。

T1

给定一个长度为 N 的只含小写英文字母的字符串 S0。令Si [j] 等于 Si−1[j] 或 Si [j − 1]。(Si [1] 总是等于 Si−1[1]) 现在给你字符串 S0 和字符串 T,你要确定最小的 i 使 Si 等于 T。如果 i 不存在输出 −1。n<=1e6。

题解:每次的变换区间相当于一条折线。贪心去做,每次修改的折线尽可能靠右,如果与前面的折线有重叠部分就多加一层,用队列维护一下就可以了。

T1AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<set>
#include<map>
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
	return x*f;
}
inline int ma(int x,int y){return x>y?x:y;}
inline int mi(int x,int y){return x<y?x:y;}
int n;
char s[1000010],t[1000010];
queue<int> q;
int main()
{
	bool fla=1;
	register int i,ml,x,ans=0,las;
	n=re_ad();scanf("%s",s+1);scanf("%s",t+1);las=n;
	for(i=1;i<=n;++i)if(s[i]!=t[i]){fla=0;break;}
	if(fla){cout<<0<<endl;return 0;}
	for(int i=n;i>=1;--i)
	{
	if(t[i]==t[i-1])continue;
	las=mi(i,las);
	while(las&&t[i]!=s[las])--las;
	if(!las){cout<<-1<<endl;return 0;}
	while(!q.empty()&&q.front()-q.size()+1>i) q.pop();
	q.push(las);
	if(i!=las)ans=ma(ans,q.size());
	}
	cout<<ans+1<<endl;return 0;
}

T2

题目大意:对于一个正整数 n,我们定义这个数的反转为将其十进制下所有数位(不包含前导零)倒序摆放后组成的新数字,记为 rev(N) 。对于给定的一个正整数 D,请求出有多少个正整数 N 满足 rev(N) = N + D。D<=1e9。

题解:将限制条件转化成小学的竖式加法减法,发现我们只需要枚举一半的数。可以使用数位 dp,也可以使用剪枝大搜索。

T2AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<set>
#include<map>
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
	return x*f;
}
int D,ln,rn,d[30];
long long ans,f[30][2][2];
inline long long solve(int x)
{
	memset(f,0,sizeof(f));f[0][0][0]=1;
	register int n=x>>1,i,j,k,num,sum,jw,sw,ji;
	register long long ret=0;
	for(i=0;i<n;++i)
	for(j=0;j<=1;++j)
	for(k=0;k<=1;++k)
	{
	if(!f[i][j][k])continue;
	for(num=0;num<=9;++num)
	{
	sum=num+d[i+1]+k;
	sw=sum%10;jw=sum/10;
	ji=(j*10+num)-(sw+d[x-i]);
	if(ji>1||ji<0)continue;
	if(!i&&(!sw||!num))continue;
	f[i+1][ji][jw]+=f[i][j][k];
	}
	}
	if(x&1)
	{
	int n1=n+1;
	for(j=0;j<=1;++j)
	for(k=0;k<=1;++k)
	for(num=0;num<=9;++num)
	{
	sum=num+d[n1]+k;if(sum%10==num&&sum/10==j)ret+=f[n][j][k];
	}
	}
	else ret+=f[n][0][0]+f[n][1][1];
	return ret;
}
int main()
{
	register int i,x;
	D=re_ad();x=D;
	while(x)ln++,d[ln]=x%10,x/=10;
	rn=ln*2;
	for(i=ln;i<=rn;++i)ans+=solve(i);
	cout<<ans<<endl;return 0;
}

T3

题目大意: N 个整数,A1-Ai。小明会把序列排成任何他想排成的样子,然后小红可以交换两个相邻且互质的数任意多次。二人绝对聪明,且小明的目的是使最终的数列 A 字典序尽量小,小红的目的是使最终的数列 A 字典序尽量大。请你求出经过操作后的最终序列。N<=2000,Ai<=1e8。

题解:首先,如果Ai与Aj不互质,那么它们的相对位置则永远不变。于是我们可以很自然地想到拓扑排序,对于每个联通快,尽量让小的数在拓扑序的前面,最后模拟小红的过程,用堆维护当前的拓扑序列即可。

T3AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<set>
#include<map>
using namespace std;
inline int re_ad()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
	return x*f;
}
int n,a[2010],ans[2010],rd[2010];
vector<int>g[2010],g2[2010];
bool vis[2010];
priority_queue<int> q;
int gcd(int x,int y){return y?gcd(y,x%y):x;}
void dfs(int x)
{
	vis[x]=true;
	register int i,v;
	for(i=0;i<g[x].size();++i)
	{
	v=g[x][i];
	if(vis[v])continue;
	g2[x].push_back(v);++rd[v];
	dfs(v);
	}
}
int main()
{
	register int i,j,x;
	n=re_ad();
	for(i=1;i<=n;++i)a[i]=re_ad();
	sort(a+1,a+n+1);
	for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)if(gcd(a[i],a[j])!=1)g[i].push_back(j),g[j].push_back(i);
	for(i=1;i<=n;++i)if(!vis[i])dfs(i);
	for(i=1;i<=n;++i)if(!rd[i])q.push(i);
	j=0;
	while(!q.empty())
	{
	x=q.top();q.pop();ans[++j]=x;
	for(i=0;i<g2[x].size();++i)
	{
	--rd[g2[x][i]];if(rd[g2[x][i]]==0)q.push(g2[x][i]);
	}
	}
	for(i=1;i<=n;++i)cout<<a[ans[i]]<<" ";
}

T4

题目大意:给出一个单词,在单词中插入若干字符使其为回文串,求回文串的个数(|s|<=200,n<=10^9)

题解:Codeforces 506E

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值