A - Tanya and Stairways CodeForces - 1005A
B - Delete from the Left CodeForces - 1005B
C - Summarize to the Power of Two
D - Polycarp and Div 3 CodeForces - 1005D
E - Median on Segments (Permutations Edition) CodeForces - 1005E1
F - Berland and the Shortest Paths CodeForces - 1005F
G - Median on Segments (General Case Edition) CodeForces - 1005E2
有一个人上台阶,台阶是递增的
求有多少个台阶以及每个台阶的最高点
【题意其实可以样例分析法】
水题 模拟
#include
#include
using namespace std;
#define LL long long
#define MAXN 1006
int N,ans;
int a[MAXN];
int main()
{
scanf("%d",&N);
int x,y=1;
for(int i=1;i<=N;i++)
{
scanf("%d",&x);
if(x==1&&i!=1) a[++ans]=y;
y=x;
}
a[++ans]=x;
printf("%d\n",ans);
for(int i=1;iprintf("%d ",a[i]);
printf("%d\n",a[ans]);
}
两个字符串,从左往右删,直到两个串相等或其中一个为空,求最少删多少
由于是从左往右删 所以显然是右边留下的字符串相等 我们对于两个字符串从右到左扫描 如果不一样则前面的全部都要删去
#include
#include
#include
using namespace std;
#define LL long long
#define MAXN 200005
char s[MAXN],t[MAXN];
int main()
{
scanf("%s\n%s",s+1,t+1);
int ls=strlen(s+1),lt=strlen(t+1);
int ns=ls,nt=lt;
while(1)
{
if(ns<1){ printf("%d\n",nt); return 0;}
if(nt<1){ printf("%d\n",ns); return 0;}
if(s[ns]!=t[nt])
{
printf("%d\n",nt+ns);
return 0;
}
ns--,nt--;
}
return 0;
}
给你n个数,问最少去掉几个数后,每个数都能找到另一个数与它相加之和为2的幂
显然,原数列中,如果一个数不满足如题的条件,则要将它删除(因为这题只能删,不能加,而一个数只要不满足条件就“无可救药”)
所以就暴力啦
但是考虑到n比较大,而范围内2的幂的个数比较小,所以可以枚举2的幂,然后减去这个值,再在原数列中查找是否有这个数。
但是细节蛮多的,注意重复的情况,如果减下来是它本身而原数列里又只有一个这个数会WA。但如果特判的话又会有多个相同的数的情况。_ (:з」∠)_
#include
#include
#include
#include
using namespace std;
#define LL long long
#define MAXN 120010
const LL p[31]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,
524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824};
LL a[MAXN];
bool b[MAXN];
int N,ans;
int main()
{
scanf("%d",&N);
for(int i=0;iscanf("%lld",&a[i]);
sort(a,a+N);// _bound must be used with sorted sequence
for(int i=0;ibool f=0;
for(int j=0;j<=30;j++)
{
LL temp=p[j]-a[i];
if(temp<0) continue;
if(temp>a[N-1]) break;
int pos=lower_bound(a,a+N,temp)-a;
if(pos==i&&a[pos+1]!=a[pos]&&a[pos-1]!=a[pos]) continue;
if(a[pos]==temp)
{
f=1;
break;
}
}
if(!f) ans++;
}
printf("%d\n",ans);
return 0;
}
//detail is important
给出一个很大很大的数,可以无限次分割,求最后所有分割而成的数中能被3整除的数量最多。
能被3整除的数各个数位之和都是3的倍数 所以这个问题转化为了:最多有多少段不重叠的区间和是3的倍数??? 然后 线段树???
(然而考试中放弃了这个复杂的想法)
这道题的难点在于要不要考虑“放弃”当前这个数。(就是说让不让这个数对答案有贡献)
例如:311539 我们应该“放弃”第1个1,否则就不能得到最优解。
可是枚举妥妥地超时。
正解的话,数学推导一下:
这里有个性质,就是每三位数中至少存在1种分割能被3整除
一位数如果能被3整除,就直接ans++
前两位数如果能被3整除,也直接ans++
如果上面两种情况都不满足,那么前两位数的余数一定为(1,1)或(2,2)两种情况。对于第三位数,除以3的余数为0,1,2.
如果为0,直接就可以。
如果为1,对于余数的第一种情况,3个数合在一起就能被3整除;对于余数的第二种情况,这个数和第二个数合在一起就可以。
如果为2,对于余数的第一种情况,这个数和第二个数合在一起就可以被3整除;3个数合在一起就能被3整除。
综上,那么以下3种情况ans累计:
然后就可以O(n)统计答案了。
#include
#include
#include
#include
using namespace std;
#define LL long long
#define MAXN 200006
char s[MAXN];
int N,ans;
int main()
{
scanf("%s",s+1);
N=strlen(s+1);
int nr=0,n=0;
for(int i=1;i<=N;i++)
{
int r=(s[i]-'0')%3;
nr+=r;
n++;
if(nr==3||r==0||n==3)
{
ans++;
nr=0,n=0;
}
}
printf("%d\n",ans);
return 0;
}
给定一个长度为n的数列{p1,p2,…,pn}由{1,2,…,0}组成。(其实就是排列)
求满足中位数为m的闭区间[l,r]的个数。
(当区间内元素为偶,中位数取中间靠左的那个数)
排列不存在重复元素 虽然顺序被打乱
但是答案区间满足:比m大的数-比m小的数=0或1(偶数长度取左边)
即:比m大的数=比m小的数 或 比m大的数=比m小的数+1
用pos表示m的位置 c[i] 表示pos~N区间中 满足比m大的数-比m小的数=i (即pos左边还需要i或i+1个小于m的数[这里指“有效的小于”,因为pos左边也存在大于m的数]) 的区间个数
注意i可能为负数 可以用map或者数组平移
实现的话,可以转化一下思想,代码会好想一点。
说得浅显一点 就是大于m的数记为1 小于m的数记为-1 m记为0 求区间和为0或1
(这么一想,似乎可以用前缀和,然而我并没有,我是记录了一下,用c[i]表示pos右边值为i的区间个数)
#include
#include
#include
#include
using namespace std;
#define LL long long
#define MAXN 200005
int p[MAXN],c[2*MAXN];
int N,M,pos,cnt;
LL ans;//注意ans开long long (虽然我肯定会忘记 然后爆得可开心啦)
int main()
{
scanf("%d %d",&N,&M);
for(int i=1;i<=N;i++)
{
scanf("%d",&p[i]);
if(p[i]==M) pos=i;
}
for(int i=pos;i<=N;i++)
{
if(p[i]>M) cnt++;
if(p[i]0;
for(int i=pos;i>=1;i--)//注意必须倒序 区间是连续的
{
if(p[i]if(p[i]>M) cnt--;
ans+=c[MAXN+cnt];
ans+=c[MAXN+cnt+1];
}
printf("%lld\n",ans);
return 0;
}
然后我又用前缀和的思路写了一下,然后在23个点上TLE_ (:з」∠)_ 上面的那个实现是O(n)的,前缀和是O(n^2)的,虽然可以优化一下区间的起点最大只能到pos那儿
#include
#include
#include
#include
using namespace std;
#define LL long long
#define MAXN 200005
int p[MAXN],c[MAXN];
int N,M,pos,cnt;
LL ans;
int main()
{
scanf("%d %d",&N,&M);
for(int i=1;i<=N;i++)
{
scanf("%d",&p[i]);
if(p[i]1;
else if(p[i]>M) c[i]=1;
else c[i]=0,pos=i;
}
for(int i=1;i<=N;i++)
c[i]=c[i-1]+c[i];
for(int i=1;i<=pos;i++)
for(int j=i;j<=N;j++)
if((c[j]-c[i-1]==0||c[j]-c[i-1]==1)&&(i<=pos&&pos<=j))
ans++;
printf("%lld\n",ans);
return 0;
}
一张图有n个顶点,m条边,现在要求选择n-1条边,输出<=k个方案.
每个方案满足:顶点1到任何1个顶点的距离 总和最小
其实就是比较裸的一个最短路树的问题
参考了一下这位大佬的博客
Berland and the Shortest Paths
#include
#include
#include
#include
using namespace std;
#define MAXN 200005
#define INF 1000000000
#define LL long long
int n,m,k,cnt;
int dis[MAXN],chose[MAXN];
vectorint ,int> >G[MAXN];
vectorint ,int> >medge[MAXN];
void bfs(int s)
{
fill(dis,dis+MAXN,INF);
dis[s]=0;
queue<int> que;
que.push(s);
while(!que.empty())
{
int u=que.front(); que.pop();
for(int i=0;iint v=G[u][i].first,id=G[u][i].second,stp=dis[u]+1;
if(stp>dis[v]) continue;
medge[v].push_back(make_pair(u,id));
if(stp!=dis[v])
dis[v]=stp,que.push(v);
}
}
}
void dfs(int u)//找方案
{
if(u==n+1)
{
cnt++;
for(int i=1;i<=m;i++)
printf("%d",chose[i]);
printf("\n");
return ;
}
for(int i=0;i1;
dfs(u+1);
chose[medge[u][i].second]=0;
if(cnt==k)
return ;
}
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d %d",&u,&v);
G[u].push_back(make_pair(v,i));
G[v].push_back(make_pair(u,i));
}
bfs(1);
LL ans=1;
for(int i=2;i<=n;i++)
{
ans*=medge[i].size();
if(ans>k)
break;
}
printf("%lld\n",min(k*1LL,ans));
dfs(2);
return 0;
}
题意与E题一样
但是这道题有多个M
照着原来的方法多搞几下吧,超不超时先不说,关键是要考虑区间可能会有重叠部分┓( ´∀` )┏
这题看了题解,思路挺清晰的,但我不会莫队_ (:з」∠)_
所以后面那个优化没怎么懂,便予诸位看官一览(后有我本人的代码):
以下来源于题解:
首先我们计算出solve(m):中位数大于等于m的方案数区间数,那么最后答案就是solve(m) - solve(m+1)
那么怎么计算sovle(m)呢?
对于一个区间[l,r],如果它的中位数大于等于m,那么这个区间中 (大于等于m的数的个数) > (小于m的数的个数)
如果记a[i]大于等于m为+1,小于m 为 -1,即 sum(l, r) > 0
————其实这里以上就是之前的思路————————
我们枚举右端点 i ,并且同时计算sum(1, i) ,那么对于这个右端点,我们只要找到之前的 sum 中 < sum(1, i)的个数(左端点的个数),这个可以用树状数组维护
但是我们有一个O(n)的方法求,用了类似莫队的方法,记s[i]为之前的sum为i的个数,add为上一个小于sum(1, i-1)的个数,对于当前的sum,
如果它要加1,add += s[sum], sum++
如果它要减1,sum –, add -= s[sum]
这样得出的add就是当前的小于sum(1, i)的个数
对于那个优化的解释,我找到了一个更为浅显易懂的版本:
now表示前缀和,delta表示前面有多个位置可以满足区间和大于0,sum[x]表示前缀和为x的个数,那么新加入一个1时,delta显然会增加sum[now]个,然后now++。 加入一个-1时,delta会减少sum[now-1]个,now–
感谢这位大佬:
https://www.cnblogs.com/hua-dong/p/9291507.html
2个结合起来看应该就差不多了
代码:
#include
#include
using namespace std;
#define MAXN 1000005
#define LL long long
int a[MAXN],sum[MAXN];
LL ans;
int n,m;
LL f(int x)//定义f(x)为中位数大于等于x的区间个数
{
memset(sum,0,sizeof(sum));//sum[i]表示前缀和为i的区间个数
int now=n;//前缀和,这里实际上是用了坐标平移
LL res=0/*答案*/,delta=0;//前面有多少个位置可以满足区间和大于0
sum[now]=1;
for(int i=1;i<=n;i++)
{
if(a[i]>=x) delta+=sum[now],now++;//加入一个1时,delta会增加sum[now]个
else now--,delta-=sum[now];//加入一个-1时,delta会减少sum[now]个
res+=delta;
sum[now]++;
}
return res;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
printf("%lld\n",f(m)-f(m+1));
}