第一题 Mushroom的序列 大意:
给一个序列,求一段连续最长区间满足:最多改变一个数,使得区间是严格的上升子序列。
解:
直接扫描一遍,记一个最长上升子序列编号。然后从每一个编号为1 的点来判断是否可以将两个序列合并,有两种情况,讨论需要注意多种子情况。。。我可能想的比较复杂,所以第一遍写的时候少考虑了一些相等的情况,就WA 了一个点。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100005
using namespace std;
int n,num[maxn],f[maxn],last=,l[maxn],pre[maxn],ans;
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
cin>>n;
for (int i=;i<=n;i++)
{
scanf("%d",&num[i]);
if (i==) f[i]=;
else {
if (num[i]>num[i-]) f[i]=f[i-]+;
else {
f[i]=;pre[i]=last;
l[last]=f[i-];last=i;
}
}
}
l[last]=f[n];
ans=;
for (int i=;i<=n;i++)
if (l[i]){
ans=max(l[i],ans);
if (i!=&&num[i+]<num[i-]){
if (num[i]>num[i-]+) ans=max(ans,l[pre[i]]+l[i]);
else ans=max(ans,max(l[pre[i]]+,l[i]+));
}
else if (i!=&&num[i+]>=num[i-]){
if (num[i+]>num[i-]+) ans=max(ans,l[pre[i]]+l[i]);
else if (num[i]>num[i-]+) ans=max(ans,l[pre[i]]+l[i]);
else ans=max(ans,max(l[pre[i]]+,l[i]+));
}
else ans=max(ans,max(l[pre[i]]+,l[i]+));
}
if (ans>n) ans=n;
cout<<ans;
return ;
}
第二题 Mushroom的区间 大意:
一个初始全部为0长度为n的序列,给m个区间,对于区间[l,r]的处理为翻转区间内的数 1->0, 0->1,问这m个区间操作能得到多少个不同的序列(即可以只进行一个区间操作也可以对序列进行多个区间翻转操作得到新序列)。如:当n=3,m=3,区间依次为:1 1 ,2 2 , 3 3 的时候有8种不同的序列。
解:
我们可以通过画图推导出如果没有判重,那么新序列数与n没有关系,=2^m。但是需要判重,有两种情况。
第一种:(横线代表区间)
1
2
3
4
因为 1 区间的翻转等效于 2+3+4 区间的翻转,所以这个情况要排除。同时1+2+3+4也是重复的排除,那么ans不乘2。
第二种:
1
2
3
4
因为2+3+4区间翻转等于翻转 1区间,因为2、3、4它们的重合部分翻转抵消了。
所以,如果把一个区间的 L ,R 当做一条边的端点,在链上连边(双向),如果回到已经到过的点,说明是重复的,那么这样我们可以用并查集来表示,用父亲节点判断是不是走过的就可以了。比较巧妙,值得学习。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100005
#define P 1000000007
using namespace std;
int n,m,fa[maxn],ans=;//***初值
int find(int x)
{
if (x==fa[x]) return x;
else return fa[x]=find(fa[x]);//return
}
int main()
{
freopen("seg.in","r",stdin);
freopen("seg.out","w",stdout);
cin>>n>>m;
for (int i=;i<=n+;i++)// n+1
fa[i]=i;
for (int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int a=find(x);
int b=find(y+);//平移右边界
if (a!=b){
ans=(ans*)%P;
fa[a]=b;
}
}
cout<<ans;
return ;
}
第三题 来自风平浪静的明天 大意:
好吧,太扯,不想写。但是其实他的题目更扯。。。
【题目描述】
冬眠了五年,光终于从梦中醒来。
千咲、要,大家都在。
隐约记得“昨天”的海船祭,爱花意外成为贡女,沉入海底。
海面冰封,却有丝丝暖流在冰面之下涌动。
此时,爱花沉睡在祭海女神的墓地。她的胞衣在一点点脱落,化作一簇簇暖流,夹杂着她的感情,向海面上涌去。
爱花,你在哪里?
五年之后,纺已经成为海洋学研究科的大学生。
在纺的帮助下,光得知了海面下海流的情况。
纺告诉光,暖流一旦产生,就会不断地向四周扩散,直到遇到海中的岩石。
红腹海牛,快告诉光,爱花在哪里。
纺帮你绘制了一张海流情况图,长度为N,宽度为M。
海很大,一边有沙滩,一边一望无际,但长度和宽度都不会超过300。沙滩是金黄色的,所以用Y表示。海是蓝色的,所以用B表示。暖流很暖和,所以用H表示
海中有大大小小的石头。石头很危险,所以用X表示
光相信自己一定能找到爱花(爱花的位置只有一种可能)
【输入格式】
第一行包括两个整数N,M。
接下来N行,每行M个字符。
【输出格式】
仅一行,表示爱花的位置(如果你无能为力,请输出 -1 ,只要你尽力,光不会责怪你)
【样例输入】
5 5
YYYHB
YYHHH
YHHXB
BBHBB
BBBBB
【样例输出】
2 3
【数据范围】
对于30%的数据,n,m<=10
对于70%的数据,n,m<=100
对于100%的数据,n,m<=300
【样例解释】
在(2,3)出现第一个H后,经过3s后,出现样例输入的地图。
P.S. Mushroom拜托他GF出的这题= =
解:
考试的时候想的是直接枚举每一个H ,然后判断它是不是。判断的时候我用的dfs,记一个层数,对于有两个数组记录每一个层数有没有H,有没有B,如果存在有一层同时有H,同时有B,那这个就不是ans。但是有一个致命的缺陷就是,有可能对于一个非ans的点,从它开始遍历刚好可以把所有点遍历完,而且没有访问到B,这个时候,我就输出了,然后WA 了9个点。。、所以可以用BFS 解决这个问题。但有可能会超时一个点,再改成手写队列就可以了。
其实可以这样想:找出那一片H的边界,即与B相连 的 H,把它 “变成B” ,然后继续更新新的边界H,以此类推,最后一个就是中心。用一个sco数组来记录,初值是-1,B是0,先用B来更新它旁边的H为1,然后再更新下一个边界H 的值为2,最后最大的那个sco值为中心。但这样也有问题,如果有一边全是不能走的X,Y ,那么有可能在推的时候会把一个夹在这X,Y中间的H 认为是中心。所以在最大sco值之中要选一个周围H 最多的作为中心。代码我还没来得及改。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define maxn 305
using namespace std;
const int zl[][]={{,,-,},{,,,-}};
int num,n,m,mp[maxn][maxn],sco[maxn][maxn],ans1,ans2;
void dfs(int d)
{
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
if (mp[i][j]==&&sco[i][j]==-){
for (int k=;k<=;k++)
{
int x=i+zl[][k],y=j+zl[][k];
if (sco[x][y]!=-&&sco[x][y]<=d){
sco[i][j]=d+;
num--;
if (num==) return ;
break;
}
}
}
dfs(d+);
}
int main()
{
freopen("calm.in","r",stdin);
freopen("calm.out","w",stdout);
cin>>n>>m;
memset(sco,-,sizeof(sco));
for (int i=;i<=n;i++)
{
char a[maxn];
scanf("%s",a);
for (int j=;j<m;j++)
{
if (a[j]=='Y'||a[j]=='X') mp[i][j+]=;
else if (a[j]=='H') {
mp[i][j+]=;num++;
}
else{
mp[i][j+]=;
sco[i][j+]=;
}
}
}
dfs();
int ma=,mm=-;
for (int i=n;i>=;i--)
for (int j=m;j>=;j--)
if (sco[i][j]>=ma){
ma=sco[i][j];
ans1=i;ans2=j;
}
cout<<ans1<<" "<<ans2;
return ;
}