11.12的一套题
题目名称 加密 冒泡排序图 重建
可执行文件名 encrypt bubble rebuild
输入文件名 encrypt.in bubble.in rebuild.in
输出文件名 encrypt.in bubble.out rebuild.in
每个测试点时限 1 秒 1 秒 1 秒
内存限制 512MB 512MB 512MB
测试点数目 10 20 10
每个测试点分值 10 5 10
是否有部分分 否 否 否
题目类型 传统型 传统型 传统型
加密
【问题描述】
有一种不讲道理的加密方法是:在字符串的任意位置随机插入字符。相应的,
不讲道理的解密方法就是从字符串中恰好删去随机插入的那些字符。
给定原文s和加密后的字符串t,求t有多少子串可以通过解密得到原文s。
【输入格式】
输入第一行包含一个字符串t,第二行包含一个字符串s。
【输出格式】
输出一行,包含一个整数,代表可以通过解密得到原文的t的子串的数量。
【样例输入】
abcabcabc
cba
【样例输出】
9
【样例解释】
用[l, r]表示子串开头结尾的下标(从 0 开始编号),这 9 种方案是:
[0,6],[0,7],[0,8],[1,6],[1,7],[1,8],[2,6],[2,7],[2,8]
【数据规模和约定】
对于30%的数据, |t| ≤ 1000。
对于100%的数据, 1 ≤ |t| ≤ 300,000, 1 ≤|s|≤ 200。
/*
没开 long long
WA 7个点
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 300010
using namespace std;
int L,R,last,ans;
int l1,l2;
char t[maxn],s[maxn];
int main()
{
freopen("encrypt.in","r",stdin);
freopen("encrypt.out","w",stdout);
scanf("%s%s",t+1,s+1);
l1=strlen(t+1);
l2=strlen(s+1);
int now=1,i=1;
while(i<=l1)
{
if(t[i++]==s[now])now++;
if(now==l2+1)
{
int fnow=l2;R=i-1;
for(int j=R;j>=1;j--)
{
if(t[j]==s[fnow])fnow--;
if(fnow==0)
{
L=j;
break;
}
}
ans+=(L-last)*(l1-R+1);
now=1,last=L,i=L+1;
}
}
printf("%d\n",ans);
return 0;
}
/*
AC代码
模拟 刚开始写对了 没开long long......
在t中找到所有最小的区间可以找到s
则答案为
ans=sum{(L-lastL)*(n-R+1)}
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define maxn 300010
using namespace std;
LL L,R,last,ans;
LL l1,l2;
char t[maxn],s[maxn];
int main()
{
freopen("encrypt.in","r",stdin);
freopen("encrypt.out","w",stdout);
scanf("%s%s",t+1,s+1);
l1=strlen(t+1);
l2=strlen(s+1);
LL now=1,i=1;
while(i<=l1)
{
if(t[i++]==s[now])now++;
if(now==l2+1)
{
LL fnow=l2;R=i-1;
for(LL j=R;j>=1;j--)
{
if(t[j]==s[fnow])fnow--;
if(fnow==0)
{
L=j;
break;
}
}
ans+=(L-last)*(l1-R+1);
now=1,last=L,i=L+1;
}
}
cout<<ans<<endl;
return 0;
}
冒泡排序图
【问题描述】
有一段使用冒泡排序产生一张图的伪代码如下:
function bubbleSortGraph(n, a[]):
graph = emptyGraph()
repeat
swapped = false
for i = 1 to n - 1:
if a[i] > a[i + 1]:
graph.addEdge(a[i], a[i + 1])
swap(a[i], a[i + 1])
swapped = true
until not swapped
return graph
函数的输入为长度为n的排列a[],输出为一张n个节点的无向图。 函数中,emptyGraph()创造了一张空的无向图, addEdge(x, y)向图中添加了一条 x 和 y 之间的无向边,最后返回的 graph 即产生的无向图。 图的点独立集为图中节点集合的一个子集。如果集合S是图G的点独立集, 那么S中任意两个节点在图𝐺中都没有边直接相连。 给定1~n的一个排列,请求出按照伪代码生成的无向图的最大点独立集的大 小,以及一定会存在于最大点独立集中的节点。
【输入格式】
输入第一行包含一个整数n。
接下来一行包含n个空格分隔的整数,代表序
列a[]。
【输出格式】
输出两行。
第一行包含一个整数,代表生成的无向图的最大点独立集的大小。
第二行输出最大点独立集中一定会包含的节点在输入序列中对应的下标, 按照从
小到大的顺序输出, 以空格分隔。
【样例输入】
3
3 1 2
【样例输出】
2
2 3
【数据规模和约定】
对于30%的数据, n≤ 16。
对于60%的数据, n≤ 1000。
对于100%的数据, 1 ≤ n≤ 100,000。
【提示】
一定存在于最大点独立集中的节点数未必等于最大点独立集的大小。
/*
......
理解错了第二问把所有的在一个LIS中的点都输出出来了
......
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100010
using namespace std;
int n,tot;
int a[maxn],c[maxn],f[maxn],maxx[maxn],p[maxn];
int init()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(x=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int main()
{
freopen("bubble.in","r",stdin);
freopen("bubble.out","w",stdout);
n=init();
for(int i=1;i<=n;i++)
a[i]=init();
for(int i=1;i<=n;i++)
{
if(a[i]>=c[tot])
{
c[++tot]=a[i];
f[i]=tot;
}
else
{
int pos=upper_bound(c+1,c+tot+1,a[i])-c;
c[pos]=a[i];f[i]=pos;
}
}
printf("%d\n",tot);
for(int i=n;i>=1;i--)
{
if(f[i]==tot||maxx[f[i]+1]>=a[i])
{
p[i]=1;
maxx[f[i]]=max(maxx[f[i]],a[i]);
}
}
for(int i=1;i<=n;i++)
if(p[i])printf("%d ",i);
printf("\n");
return 0;
}
/*
AC代码:
和刚开始写的差了两句话
第一问为求LIS
求出第一问后
现在即要求所有LIS中都包含的元素
我们记录所有LIS中出现过得元素 并记录LIS中每个长度的出现次数
答案即为在LIS中出现并且长度的为1的位置
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100010
using namespace std;
int n,tot;
int a[maxn],c[maxn],f[maxn],maxx[maxn],p[maxn],sum[maxn];
int init()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(x=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int main()
{
freopen("bubble.in","r",stdin);
freopen("bubble.out","w",stdout);
n=init();
for(int i=1;i<=n;i++)
a[i]=init();
for(int i=1;i<=n;i++)
{
if(a[i]>=c[tot])
{
c[++tot]=a[i];
f[i]=tot;
}
else
{
int pos=upper_bound(c+1,c+tot+1,a[i])-c;
c[pos]=a[i];
f[i]=pos;
}
}
printf("%d\n",tot);
for(int i=n;i>=1;i--)
{
if(f[i]==tot||maxx[f[i]+1]>=a[i])
{
p[i]=1;sum[f[i]]++;
maxx[f[i]]=max(maxx[f[i]],a[i]);
}
}
for(int i=1;i<=n;i++)
if(p[i]&&sum[f[i]]==1)
printf("%d ",i);
printf("\n");
return 0;
}
重建
【问题描述】
给定一个N个点M条边的有向图。你可以选择一个节点x,然后重建所有能
从x到达,而且能到达x的所有节点(包括x自身)。此外,你还可以先将一条边
改成双向边,然后再进行上面的选择。
请你求出最多可以重建的节点数,并求出通过选择哪些边改成双向边才能使
重建的节点达到最多。
【输入格式】
输入的第一行包含两个整数N和M。
接下来M行,每行包含两个整数u和v,描述一条有向边。
保证图中任意两点在任意方向上最多只有一条边直接相连。
【输出格式】
输出三行。第一行输出一个整数, 最多可以重建的节点数。
第二行输出一个整数K,代表有K条边能使重建节点数达到最多。
第三行输出K个整数,代表可以选择的边的编号。边按照输入顺序从 1 开始
编号。请按照从小到大的顺序输出,并以空格分隔。
【样例输入 1】
5 4
1 2
2 3
1 3
4 1
【样例输出 1】
3 1 3
【样例输入 2】
3 4
1 2
2 1
1 3
3 1
【样例输出 2】
3 4
1 2 3 4
【数据规模和约定】
对于30%的数据, N, M ≤ 10。
对于60%的数据, N ≤ 1500, M ≤ 100,000。
对于100%的数据, 1 ≤ N ≤ 2000, M ≤ N × N。
/*
暴力而且有bug
找了一下午才找到
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxx 2010
#define maxn 4000010
using namespace std;
int n,m,topt,top,num,tot,ans,anst;
int dis[maxx],p[maxn];
int first[maxx],head[maxx],sum[maxx];
int dfn[maxx],low[maxx],s[maxx],f[maxx],belong[maxx];
struct edge
{
int to;int next;
int w;
}e[maxn],r[maxn];
queue<int>q;
int init()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(x=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
void add(int x,int y,int z)
{
topt++;
e[topt].to=y;
e[topt].w=z;
e[topt].next=first[x];
first[x]=topt;
}
void Add(int x,int y,int z)
{
tot++;
r[tot].to=y;
r[tot].w=z;
r[tot].next=head[x];
head[x]=tot;
}
void tarjan(int now)
{
dfn[now]=low[now]=++topt;
f[now]=1;s[++top]=now;
for(int i=first[now];i;i=e[i].next)
{
int to=e[i].to;
if(!dfn[to])
{
tarjan(to);
low[now]=min(low[now],low[to]);
}
else if(f[to])
low[now]=min(low[now],dfn[to]);
}
if(dfn[now]==low[now])
{
num++;
while(s[top]!=now)
{
belong[s[top]]=num;
f[s[top]]=0;
sum[num]++;
top--;
}
belong[s[top]]=num;
f[s[top]]=0;
sum[num]++;
top--;
}
}
void spfa(int x)
{
for(int i=1;i<=num;i++)dis[i]=f[i]=0;
dis[x]=sum[x];f[x]=1;q.push(x);
while(!q.empty())
{
int now=q.front();q.pop();f[now]=0;
for(int i=head[now];i;i=r[i].next)
{
int to=r[i].to;
if(dis[to]<dis[now]+sum[to])
{
dis[to]=dis[now]+sum[to];
if(!f[to])
{
f[to]=1;
q.push(to);
}
}
}
}
for(int i=head[x];i;i=r[i].next)
{
int to=r[i].to;
p[r[i].w]=max(p[r[i].w],dis[to]);
if(dis[to]>ans)
{
ans=dis[to];
anst=1;
}
else if(dis[to]==ans)
anst++;
}
}
int main()
{
freopen("rebuild.in","r",stdin);
freopen("rebuild.out","w",stdout);
n=init();m=init();
for(int i=1;i<=m;i++)
{
int x,y;
x=init();y=init();
add(x,y,i);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=1;i<=n;i++)
{
for(int j=first[i];j;j=e[j].next)
{
int to=e[j].to;
if(belong[i]!=belong[to])
Add(belong[i],belong[to],e[j].w);
else
{
p[e[j].w]=max(p[e[j].w],sum[belong[i]]);
if(ans<sum[belong[i]])
{
ans=sum[belong[i]];
anst=1;
}
else if(ans==sum[belong[i]])
anst++;
}
}
}
for(int i=1;i<=num;i++)
spfa(i);
printf("%d\n%d\n",ans,anst);
for(int i=1;i<=m;i++)
if(p[i]==ans)printf("%d ",i);
printf("\n");
return 0;
}
正解:无