T1,T2
都是蓝书上贪心专题例题,看书就行了
T3
【题目描述】
在一个字符串S上应用以下操作K次,可以得到多少个不同的字符串? 选择一个小写的英文字母并将它插入到S的某个位置
答案可能是巨大的,所以输出答案模(1e9+7)的值。
【输入格式】string.in
第一行输入一个整数K(1≤K≤1e6)。
第二行输入1个字符串S。S由小写英文字母组成,并且满足:1≤|S|≤1e6。
【输出格式】string.out
输出一个整数,即不同的字符串数量模(1e9+7)的值。
【输入样例1】
5
oof
【输出样例1】
575111451
【样例1说明】
例如,我们可以获得proofend、moonwolf和onionpuf,
而不能获得oofsix、oofelevennn、voxafolt或foooooo。
——————————————————————————
拿到这题,我第一反应是直接加字符,26*26这样乘下去,但这样会有重复的字符串产生,并且我不知道如何去算重复的个数。
于是有一个很巧妙的做法,首先像样例1如果产生了aoofovof这样的字符串,我们默认最后的“ovof”中的oof是原本的oof,就将第一个o为分界线,原先字符串被分为“aoof”和"ovof"两部分,并且我们发现前面一段中字母可以随便取,于是当在前面的字母数为i时,临时的ans便乘上quick_pow(26,i),后面的一段因为只有1个"oof",所以在往里面加字母时不能加原串中的上一个字母,所以每个位置字母只能选25种,于是ans再乘以quick_pow(25,n-i),然后考虑后面字母的位置,便是需要排列组合公式:
又因为右边字符串第一个字母是固定了的,所以ans再乘以C(n+m-i-1,m-1)。
附上std
#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define LL long long
#define maxn 1000010
char s[maxn];
LL fact[maxn<<1],inv[maxn<<1];
int m,n;
LL ksm(LL a, LL b) { return (b % 2 == 1? a : 1LL) * (b == 0? 1LL : ksm(a*a%mod, b/2)) % mod; }
LL quick_pow(LL a,LL b) //快速幂
{
LL ret=1;
while(b)
{
if(b&1)ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret%mod;
}
void init()
{
fact[0]=1;
for(int i=1;i<=n+m;i++)
fact[i]=fact[i-1]*i%mod; //阶乘
inv[n+m]=quick_pow(fact[n+m],mod-2);
for(int i=n+m-1;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%mod; //阶乘的乘法逆元
}
LL C(int n,int m) //组合数计算
{
return fact[n]*inv[n-m]%mod*inv[m]%mod;
}
int main()
{
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
LL ans=0;
scanf("%d%s",&n,s);
m=strlen(s);
init();
for(int i=0;i<=n;i++)
ans=(ans+quick_pow(26,i)*C(n+m-i-1,m-1)%mod*quick_pow(25,n-i)%mod)%mod;
printf("%lld\n",ans);
return 0;
}
T4
有一棵有N个结点的树,第i条边双向连接结点Ai和Bi。
Tom站在结点u,Jerry站在结点v。
现在,他们将玩一个标记游戏,玩法如下所示:
(1)如果Tom和Jerry站在同一个结点,游戏结束;否则,Tom移动到他选择的结点,
该结点与他当前的结点相邻。
(2)如果Tom和Jerry站在同一个结点,游戏结束;否则,Jerry移动到他选择的结
点,该结点与他当前的结点相邻。
(3)回到步骤(1)。
轮到某人走时,不能停留在同一个结点。
Tom希望回合数尽可能多,而Jerry则希望回合数尽可能少,两人都知道对方的位置和
策略,并且自己都采用最优策略。
可以证明这场比赛一定会结束。
输出游戏结束时Jerry走的步数。
【输入样例】
第一行输入三个正整数N,u,v,相邻数之间由一个空格隔开。
接下来有N-1行,每行两个正整数Ai和Bi,表示结点A和结点B有一条双向边相连。
【输出格式】
输出一个整数,即游戏结束时Jerry走的步数。
【输入样例1】
5 4 1
1 2
2 3
3 4
3 5
【输出样例1】
2
【样例1说明】
如果两个玩家都玩得最好,游戏将按如下方式进行:
•Tom移动到结点3。
•Jerry移动到结点2。
•Tom移动到结点5。
•Jerry移动到结点3。
•Tom移动到结点3。
在这里,Jerry执行了两个操作步骤。
题解
首先,Jerry的最优策略很好确定,就是走通向的当前Tom的位置的简单路径就可以了。同时,不论Tom的位置怎样变化,Jerry走的路径都会沿着通向最终会抓到Tom的结点的这条道路,证明很难,我就不证明了。感性地理解,就是由于Tom和Jerry不会在途中相遇,所以Jerry走向的那个子树(以当前Jerry所在结点为根)肯定包含Tom和游戏结束时的Tom的终点。
那么Tom的策略呢就有点麻烦,所以我们就用搜索判断:
首先,我们预处理出Jerry到每个结点的距离,之后搜索Tom的路径(深搜、广搜皆可,反正别学我个憨憨用lca )。对于当前结点是否可以走,只需要满足Tom到这里的距离严格小于Jerry到这里的距离即可。特殊地,由于只能移动,不能停留在同一个结点,所以到了叶子结点时,Tom会在相邻结点和这个叶子结点之间重复移动,最终抓到的位置一定是在相邻结点上(一共两种情况,自己手推一下就可以发现)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int ans,du[N],tot,n,u,v,first[N],Next[N*2],to[2*N],x,y,dis[N],sum,l,r,q[N];
void add(int a,int b)
{
tot++;
Next[tot]=first[a];
first[a]=tot;
to[tot]=b;
}
void dfs(int st,int fa)
{
if(sum>=dis[st])
{
ans=max(ans,dis[st]);
return;
}
for(int i=first[st];i;i=Next[i])
{
if(to[i]==fa)
continue;
sum++;
dfs(to[i],st);
sum--;
}
if(du[st]==1)
ans=max(ans,dis[st]-1);
}
int main()
{
cin>>n>>u>>v;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x),du[x]++,du[y]++;
}
memset(dis,-1,sizeof(dis));
dis[v]=0;
r++;
q[r]=v;
while(l<r)
{
l++;
int U=q[l];
for(int i=first[U];i;i=Next[i])
{
int V=to[i];
if(dis[V]==-1)
{
dis[V]=dis[U]+1;
r++;
q[r]=V;
}
}
}
dfs(u,0);
cout<<ans;
return 0;
}
T5
【题目描述】
对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:例如一数列4 2 4 5 1要分成3段将其如下分段:[4 2][4 5][1]第一段和为6,第2段和为9,第3段和为1,和最大值为9。 将其如下分段:[4][2 4][5 1]第一段和为4,第2段和为6,第3段和为6,和最大值为6。 并且无论如何分段,最大值不会小于6。所以可得到要将数列4 2 4 5 1要分成3段,每段和的最大值最小为6。
【输入格式】
输入文件divide.in的第1行包含两个正整数N,M;
第2行包含N个空格隔开的非负整数A[i],含义如题目所述。
【输出格式】
输出文件divide.out仅包含一个正整数,即为每段和最大值的最小值。
【样例输入】
5 3
4 2 4 5 1
【样例输出】
6
【数据范围】
对于20%的数据,有N≤10;
对于40%的数据,有N≤1000;
对于100%的数据,有N≤100000;M≤N;A[i]之和不超过1e9.
少见的最后一题是水题…问最大值的最小值,很显然是二分,而且都是正整数,就直接做了
附考场代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,l,r,mid,a[N],tot,sum,Max;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int Get(int x)
{
tot=0;
sum=0;
for(int i=1;i<=n;i++)
{
sum+=a[i];
if(i==n)
tot++;
if(sum>x)
{
sum=a[i];
tot++;
}
}
return tot;
}
int main()
{
//freopen("divide.in","r",stdin);
//freopen("divide.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read(),sum+=a[i],Max=max(Max,a[i]);
l=0;
r=sum;
while(l<r)
{
mid=(l+r)>>1;
if(mid<Max)
{
l=mid+1;
continue;
}
if(Get(mid)>m)
l=mid+1;
else
r=mid;
}
cout<<l;
}
难得写了次博客啊…