B -Deque
题意:给你一个序列,和一个可以往两边放的队列,要求按输入顺序把序列中的数放到队列中去,可以拿出队列中的元素,问最后使得在队列中的元素是非递减序列的最长长度是多少
思路:这相当于求最长上升子序列(非递减),由于队列两端都可以放,则相当于拿到一个数,就以这个数为开始的最长非递减子序列+以它为开始的最长非递增子序列-两者的重复个数
不管怎样,手动模拟还是硬道理。。。上次多校赛后就模拟的很顺利,以为懂了,昨天又做了这道题,我发现自己又有点迷糊了,之前不知道为什么从后面模拟,昨天想了好久(一个晚上),。。似乎久了点。。。发现原来从后面回来到达中间的某一个数时,就直接可以求以这个点为开始的最长非递减子序列和以它为开始的最长非递增子序列,因为题目要求是按顺序(从前往后的)放队列的。。。。神题。。
代码:
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string.h>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int M=100005;
int down[M];
int up[M];
int cf1[M],cf2[M];
int a[M];
int Bin1(int key,int l,int r)
{
while(l<=r)
{
int mid=(l+r)>>1;
if(up[mid]>=key)l=mid+1;
else
r=mid-1;
}
return l;
}
int Bin2(int key,int l,int r)
{
while(l<=r)
{
int mid=(l+r)>>1;
if(down[mid]<=key)l=mid+1;
else
r=mid-1;
}
return l;
}
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
memset(cf1,0,sizeof(cf1));
memset(cf2,0,sizeof(cf2));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
up[1]=a[n];
down[1]=a[n];
cf1[1]=1;
cf2[1]=1;
int p1=1,p2=1;
int mmax=1;
for(int i=n-1;i>=1;i--)
{
int pos1=Bin1(a[i],1,p1);
if(pos1>p1)p1++;
up[pos1]=a[i];
cf1[pos1]=(pos1>1 && a[i]==up[pos1-1])?cf1[pos1-1]+1 : 1;
int pos2=Bin2(a[i],1,p2);
if(pos2>p2)p2++;
down[pos2]=a[i];
cf2[pos2]=(pos2>1 && a[i]==down[pos2-1])? cf2[pos2-1]+1 : 1;
mmax=max(mmax,pos1+pos2-min(cf1[pos1],cf2[pos2]));
}
printf("%d\n",mmax);
}
return 0;
}
D - I-number
题意:给出一个数x,求一个最小的y>x并且y%10==0
题比较简单,不过重新做的时候还是WA了,注意细节!
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include<cmath>
using namespace std;
const int M=100005;
char a[M];
int s[M];
int main()
{
int t,i;
scanf("%d",&t);
getchar();
while(t--)
{
memset(s,0,sizeof(s));
scanf("%s",a);
int l=strlen(a);
int j=0;
for(i=l-1; i>=0; i--)
{
s[j++]=a[i]-'0';
}
int sum;
do
{
sum=0;
s[0]+=1;
for(i=0; i<l; i++)
{
s[i+1]+=s[i]/10;
s[i]=s[i]%10;
}
if(s[l])
l++;
for(i=0; i<l; i++)
sum+=s[i];
}
while(sum%10!=0);
for(i=l-1; i>=0; i--)
printf("%d",s[i]);
printf("\n");
}
return 0;
}
H - Palindrome Sub-Array
题意:从一个n*m的矩阵中选一个P*P的正方形,使得正方形里的每一行每一列都是回文
这题之前使劲的在纸上计算,花了很长时间,我可以说这次的速度还可以,不过WA了,可郁闷了,后面找了好久,才发现错误,又长知识了~~
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string.h>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int M=100005;
int a[350][350];
bool panduan(int x,int y,int len)
{
for(int i=0; i<len; i++)//写成了len/2-1 WA
{
for(int j=0; j<=len/2-1; j++)
{
if(a[x+i][y+j]!=a[x+i][y+len-j-1])return false;
if(a[x+j][y+i]!=a[x+len-j-1][y+i])return false;
}
}
return true;
}
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
scanf("%d",&a[i][j]);
}
// for(int i=1; i<=n; i++)
// {
// for(int j=1; j<=m; j++)
// printf("%d ",a[i][j]);
// printf("\n");
// }
int len=min(n,m) ,l,ff=0;
for(l=len ; l>=1 ; l--)
{
for(int i=1; i+l<=n+1; i++)
{
for(int j=1; j+l<=m+1; j++)
{
if(panduan(i,j,l))
{
ff=1;
break;
}
}
if(ff==1)break;
}
if(ff==1)break;
}
printf("%d\n",l);
}
return 0;
}
I -Warm up 2
题意:有这样放的多骨诺牌,垂直放和竖着放的,相同方向的不会相交,只有水平和垂直方向的牌会相交,问最少去掉几个牌使得剩下的牌互不相交
思路:把那些相交的牌连成边,然后进行二分匹配,结果就是所有牌个数减去最大匹配数
说心里话,我不太明白为什么减去最大匹配数就是答案,我只是一直记得这个结论,,额。。。就敲代码了
判断相交的时候要小心点,不然。。。我至少在纸上画了好久,原来就这么简单
如:(x1,y1)(x1+1,y1)
(x2,y2) (x2,y2+1)
其实分别把上下两个点组合判断即可!!
代码:
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string.h>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int M=1010;
struct node
{
int x,y;
} a[2*M];
int mark[M][M];
bool panduan(int i,int j)
{
if((a[i].x==a[j].x && a[i].y==a[j].y )|| (a[i].x+1==a[j].x && a[i].y==a[j].y)||
(a[i].x==a[j].x && a[i].y==a[j].y+1 )||(a[i].x+1==a[j].x && a[i].y==a[j].y+1))
return true;
else
return false;
}
int link[2*M],vis[2*M],n,m;
bool dfs(int u)
{
for(int i=n+1; i<=n+m; i++)
{
if( mark[u][i] && !vis[i])
{
vis[i]=1;
if(link[i]==-1 || dfs(link[i]))
{
link[i]=u;
return true;
}
}
}
return false;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(!n && !m)break;
memset(mark,0,sizeof(mark));
for(int i=1; i<=n; i++)
scanf("%d%d",&a[i].x,&a[i].y);//垂直
for(int i=n+1; i<=n+m; i++)
scanf("%d%d",&a[i].x,&a[i].y);//水平
for(int i=1; i<=n; i++)
{
for(int j=n+1; j<=n+m; j++)
{
if(panduan(i,j))//相交
{
mark[i][j]=1;
}
}
}
int ss=0;
memset(link,-1,sizeof(link));
for(int i=1; i<=n; i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i))
ss++;
}
printf("%d\n",n+m-ss);
}
return 0;
}
J - The Unsolvable Problem
题目太坑人了。。。到现在我又被坑了一次
题意:要求a+b=n并且a*b的最大公约数最大(我理解成了a b的最小公倍数最大时max(a,b),WA了!
思路:要使公倍数最大,最好方案是尽量使a b 互质,并且a+b==n所以a b 相差不大 ====》所以从中间找!
代码:
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string.h>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int M=100005;
int main()
{
int t;
long long s,n;
scanf("%d",&t);
while(t--)
{
scanf("%I64d",&n);
if(n==2)//要不要这么悲哀是1!
{
printf("1\n");
continue;
}
if(n%2==0)//直接写LL不行
{
n/=2;
if(n%2==0)
s=(n-1)*(n+1);
else
s=(n-2)*(n+2);
}
else
{
n/=2;
s=n*(n+1);
}
printf("%I64d\n",s);
}
return 0;
}
N - Group
题意:分组,相邻标号的是朋友,每一组的Value=k*k, k为每组的人数,问怎样分组才能使总的Value值最大,输出分了几组
思路:首先考虑树状数组求解,采用离线算法。根据Value 的定义,发现只要一组内的人数越多value值就更大,所以我们把方向放到组内人数上,要使一组内人数更多,就相当于分的组越少,也就是能分一组的尽量分一组,因此,把编号相邻的都编为一组
首先每次拿到一个人的编号,就更新(+1),然后再在前面找与它相邻的编号是否存在,若存在,找到相邻数的位置进行更新(-1)
代码:
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string.h>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int M=100005;
int n,m;
int c[M],a[M],ans[M];
int pos[2*M];
struct node
{
int s,e;
int id;
} inv[M];
bool cmp(node a,node b)
{
return a.e<b.e;
}
int lowbit(int x)
{
return x&-x;
}
void update(int x,int v)
{
for(int i=x; i<=n; i+=lowbit(i))
c[i]+=v;
}
int query(int x)
{
int sum =0;
for(int i=x; i>0; i-=lowbit(i))
sum+=c[i];
return sum;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for(int i=1; i<=m; i++)
{
scanf("%d%d",&inv[i].s,&inv[i].e);
inv[i].id=i;
}
sort(inv+1,inv+m+1,cmp);
memset(c,0,sizeof(c));
int j=1;
for(int i=1; i<=n; i++)
{
update(i,1);
if(a[i]>1 && pos[a[i]-1]<i )update(pos[a[i]-1],-1);
if(a[i]<n && pos[a[i]+1]<i )update(pos[a[i]+1],-1);
while(i==inv[j].e && j<=m)
{
ans[inv[j].id]=query(inv[j].e)-query(inv[j].s-1);
j++;
}
}
for(int i=1; i<=m; i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}
O - Hehe
字符串问题
题意:给出一个字符串,hehe代表两种意思,求这组字符串的意思有几种
例举几组hehe的连续个数,可以发现连续的hehe中h的个数服从斐波那契数列,这样就很好求了
代码:
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string.h>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int M=10100;
char a[M];
int f[M];
void ff()
{
f[1]=1;
f[2]=2;
for(int i=3; i<=M; i++)
{
f[i]=(f[i-1]+f[i-2])%10007;
//f[i]%=10007;
}
}
int main()
{
ff();//忘记加这个了囧、、、,,
int t,g=1;
scanf("%d",&t);
getchar();
while(t--)
{
memset(a,0,sizeof(a));
scanf("%s",a);
int l=strlen(a);
int s=1;
int k;
for(int i=0; i<l-2; i++)
{
if(a[i]=='h' && a[i+1]=='e')
{
int j;
k=1;
for(j=i+2; j<l; j+=2)
{
if(a[j]=='h' && a[j+1]=='e')
k++;
else
break;
}
s=s* f[k]%10007;
i=j;
}
}
printf("Case %d: %d\n",g++,s);
}
return 0;
}
P - Fliping game
博弈题(yy思想)
由于每次翻动都是从正面翻到反面(1--0),并且最右下角总是被翻动(1-0转换)所以只要谁碰到最右下角是0,那就输了,因为他只能从别的翻动,他一翻动,就又把最右下角翻成1了,给对方胜利的机会
代码:
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#define lson l,mid,num<<1
#define rson mid+1,r,num<<1|1
using namespace std;
const int M=100005;
int a[105][105];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
if(a[n][m]==1)printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}