雪崩,全错掉了GG。前两道题相对之前的难度大一点啊,不过A题有个循环应该是从0开始而不是1开始这样的低级错误不应该犯。B题差不多是一个BFS,但是我当时始终绕着最短路径写来写去,一直各种TLE与WA。C题是一道二分,挺容易的,昨天一直在写B,没做真是可惜。
A
我用DFS写的,相对比较麻烦了。就是按照它的变化次序,来把0-9都试一遍,看有没有满足的。由于那个循环不小心写成了1-9,直接崩了。哎,教训。
B
题目大意就是求从顶点1到其它所有顶点的最短路径值。每两个点之间的距离为下标的差的绝对值。另外每个点还有一个自己的ai,点i到ai的距离为1。共有20w个顶点,我想直接用最短路那儿的方法求一个单源最短路径的,但不管是SPFA(时间复杂度O(kE)),还是用堆优化的dijkstra(时间复杂度O(E+nlogn))都要超时。最后自己又尝试了一个比较怪的dfs,还是超时,到最后就一直卡在这里结束了。第二天看题解发现是用BFS写的,因为DFS扩展的状态太多(每次都有n个),很多带来了重复计算导致效率低下,实际上,由于后面处理的一定比前面处理的距离要长,那么我们只要处理3个状态,即当前状态的前一个、后一个和跳跃的那一个就好了。已经处理过的就不要再处理了。
//
// main.cpp
// Codeforces 361 A
//
// Created by 赵奕 on 16/7/7.
// Copyright © 2016年 赵奕. All rights reserved.
//
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <cmath>
const int MAX = 99999999;
int n,dis[200010],a[200010];
int flag[200010];
using namespace std;
queue<int> q,q2;
void pushing(int x,int y)
{
if (x < 1 || x > n || flag[x])
return ;
q.push(x);
q2.push(y);
flag[x] = 1;
dis[x] = y;
}
int main()
{
int i,j,s,t;
cin >> n;
memset(flag,0,sizeof(flag));
for (i = 1; i <= n; ++i)
scanf("%d",&a[i]);
q.push(1);
q2.push(0);
flag[1] = 1;
while (!q.empty())
{
s = q.front();
q.pop();
t = q2.front();
q2.pop();
pushing(s-1,t+1);
pushing(s+1,t+1);
pushing(a[s],t+1);
}
for (i = 1; i <= n; ++i)
{
cout << dis[i] << " " ;
}
return 0;
}
C
题目大意就是有四个数,设第一个数为x,那么第二、三、四个数就分别为kx,k*k*x,k*k*k*x,其中k是任意的。并且这四个数都不能大于n,然后假定符合条件的有m组。题目给定m,问满足有m对的这四个数的最小n为多少。
求最大值的最小。二分。对于每个n,对于每个x,共有[n/(x^3)]个k。让x从2到三次根号下n做一遍循环求一下就好了。
//
// main.cpp
// Codeforces 361 C
//
// Created by 赵奕 on 16/7/7.
// Copyright © 2016年 赵奕. All rights reserved.
//
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <cmath>
const int MAX = 99999999;
typedef long long ll;
ll n,dis[200010],a[200010];
using namespace std;
ll work(ll k)
{
ll i,j;
j = 0;
for (i = 2; i * i * i <= k; ++i)
{
j += k/(i*i*i);
}
return j;
}
int main()
{
ll h,i,j,l,r,mid,ans;
cin >> n;
l = 1;
r = 1e18;
ans = -1;
while (l <= r)
{
mid = (l+r) >> 1;
h = work(mid);
if (h >= n)
{
if (h==n)
ans = mid;
r = mid-1;
}
else
l = mid+1;
}
cout << ans << endl;
return 0;
}
D
二分+数据结构
题目大意是给你两个长度为20w的数组a、b,求有多少个子区间满足max{a[i]}=min{b[i]}。
我们的做法是固定住区间的右位置,来求有多少个左位置满足条件。然后把这些加起来即可。当右位置固定时,a数组关于左位置的最大值是单调不升的,b数组关于左位置是单调不降的,因此我们可以用二分法来求得有多少满足题意的子区间。我们维护这样的一个值t,使得[t,i]区间,a的最大值比b的最小值一定要小(因为,再往后,位置t仍然没有作用,我们此时把t往后挪)然后在[t,i]区间内找到a的最大值和b的最小值的位置,取它们的较小值为mp,那么从t…mp位置的[j,i]一定都是满足的。
参考小数据:
6
1 2 3 2 1 2
6 7 1 2 3 2
在第三个位置时,A中的最大值3>B中的最小值1,一步步往后拱直至A和B都空了。
在第四个位置时,A中的最大值2=B中的最小值1,此时从4到4都是满足[j,i]满足的。cnt +=1;
在第五个位置,A中的最大值2仍然=B中的最小值1,此时A中的最大值在4,B中的最小值在4,此时从4到4都是满足[j,i]满足的。 cnt+=1
在第五个位置,A中的最大值2仍然=B中的最小值1,此时A中的最大值在6,B中的最小值在6,此时从4到6都是满足[j,i]满足的。cnt+=3;
#include <cstdio>
#include <iostream>
#include <set>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long Long;
typedef pair<Long, int> PLI;
int main() {
int N;
cin >> N;
vector<int> A(N), B(N);
for(int &v : A)cin >> v;
for(int &v : B)cin >> v;
int t = 0;
Long cnt = 0;
set<PLI> SB;//按照pair的第一个元素来排,第一个相等的话按第二个来排
set<PLI,greater<PLI>> SA;//a数组是求最大值,因此按照从大到小的greater来排
for(int i = 0; i < N; ++i){
SA.insert(PLI(A[i],i));//每次把当前的数和位置插入set里
SB.insert(PLI(B[i],-i));
while(!SA.empty() && !SB.empty() && SA.begin()->first > SB.begin()->first )
{//如果当前A序列的最大值比B的最小值要大,显然[t,i]区间没有满足的,那么把t往前拱,使得把A的大的弄掉,B的小的弄掉
SA.erase(SA.lower_bound(PLI(A[t],t)));
SB.erase(SB.lower_bound(PLI(B[t],-t)));
t++;
}
if(!SA.empty() && !SB.empty() && SA.begin()->first == SB.begin()->first){//如果此时A的最大值和B的最小值相等。那么计算有多少个j使得在[j,i]满足题意。
int mp = min( SA.begin()->second, -(SB.begin()->second) );//A里面第一个存放的是t到i位置的最大值的位置
cnt += mp - t + 1;//j属于[t,mp]都满足[j,i]区间...
}
}
cout << cnt << endl;
return 0;
}