6/13 数据结构真是要命。
C.SPOJ.com - Problem NUMTRY
思路:
数论,不会证,记住定理吧。参考参考:
最大公约数和最小公倍数_ACdreamers的博客-优快云博客
之后还要pollard_rho吧大概,剩下C不动了。反正直接筛喜提tle。
G.E - Round Trip (atcoder.jp)
思路1(赛时一血!):
一开始写了标准的回溯dfs又wa又tle,后来想了想发现,如果走到某一步发现无路可走了,那必然是他走到死路上了,从其他任意 . 必不可能再次走到他,所以这个不用回溯,而且在之前搜索过的基础上继续搜索会减小很多重复程序,降低时间复杂度。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e4 + 50;
char arr[N][N];
bool vis[N][N];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
int x, y;
int n, m;
bool judge(int a, int b) {
if (a > n || b > m || a < 1 || b < 1 || vis[a][b] == 1 || arr[a][b] == '#')
return 0;
else
return 1;
}
bool dfs(int a, int b, int step) {
if (a == x && b == y && step > 1)
return 1;
vis[a][b] = 1;
for (int i = 0; i < 4; ++i) {
int tx = a + dx[i], ty = b + dy[i];
if (judge(tx, ty)) {
//cout << tx << " " << ty << '\n';
if (tx == x && ty == y && step == 0)//防止第一步走到S
continue;
if (dfs(tx, ty, step + 1))
return 1;
}
}
return 0;
}
void work() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> arr[i][j];
if (arr[i][j] == 'S') {
x = i;
y = j;
}
if (arr[i][j] == '#') {
vis[i][j] = 1;
}
}
}
for (int i = 0; i < 4; ++i) {
int tx = x + dx[i], ty = y + dy[i];
if (judge(tx, ty)) {
//cout << tx << " 0 " << ty << '\n';
if (dfs(tx, ty, 0)) {
cout << "Yes\n";
return;
}
}
}
cout << "No\n";
}
signed main() {
io;
work();
return 0;
}
/*
4 4
..##
.S.#
###.
....
*/
思路2(yjgg):
类似油田,标记相同的联通块为相同颜色,只需判断‘S'四周是否存在两个相同颜色的联通块即可。
一些其他小技巧:
- 二维数组会开不下可以用map<pair<int,int>,int>存图
- 涉及到回溯的dfs的时间复杂度一般是指数或阶乘形式的,只是遍历图的dfs时间复杂度就是图的大小,可以忽略不计。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f
const int N = 1e6 + 10;
string mp[N];
map<pair<int, int>, int>col;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int x, y;
int n, m;
bool judge(int a, int b) {
if (a > n || b > m || a < 1 || b < 1 || mp[a][b] == '#' )
return 0;
else
return 1;
}
void dfs(int a, int b, int cur) {
for (int i = 0; i < 4; ++i) {
int tx = a + dx[i], ty = b + dy[i];
if (judge(tx, ty)) {
col[m_p(tx, ty)] = cur;
mp[tx][ty] = '#';
dfs(tx, ty, cur);
}
}
}
void work() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
string a;
cin >> a;
mp[i] = '0' + a;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (mp[i][j] == 'S') {
x = i;
y = j;
mp[i][j] = '#';
}
}
}
bool flag[4] = {0};
for (int i = 0; i < 4; ++i) {
int tx = x + dx[i], ty = y + dy[i];
if (judge(tx, ty))
flag[i] = 1;
}
int tot = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (judge(i, j)) {
tot++;
col[m_p(i, j)] = tot;
mp[i][j] = '#';
dfs(i, j, tot);
}
}
}
vector<int>tt;
for (int i = 0; i < 4; ++i) {
int tx = x + dx[i], ty = y + dy[i];
if (flag[i])
tt.pb(col[m_p(tx, ty)]);
}
sort(tt.begin(), tt.end());
for (int i = 1; i < tt.size(); ++i) {
if (tt[i] == tt[i - 1]) {
cout << "Yes\n";
return;
}
}
cout << "No\n";
}
int main() {
io;
work();
}
我真的很不喜欢dfs!!!!!!!!!!
F.F - Double Chance (atcoder.jp)
思路:
数学+树状数组。
分母:从K张卡片中抽卡,所有情况总量是K*K,即期望的分母
分子:每种情况的答案的求和:
递推。由于每次增加只多了a[K]这一个卡片,即只多了2*K-1种抽法,所以K可以由K-1推来。而这2*K-1种抽法对答案的贡献可以分为两部分来计算,第一部分是x<=a[K],假设x<=a[K]的x的数量是num,这些数字对分子的贡献是(2*num+1)*a[K],第二部分是x>a[K],这些数字的对答案的贡献是这些数字的和,即前k-1个数字中出现的严格大于a[k]的所有的数字的和。
我们可以用两个树状数组分别维护num与和,对于每个答案,我们只需要再乘以i*i的逆元即可得到期望。请务必注意取模。
看题解看了几天发现其实我根本不算会树状数组和线段树,简单说说。
这个题其实只用了单点修改和区间查询,我们在递推的过程中建树,并且一定要先算数再加点。注意到建树的顺序和数的大小并无关系,所以令arr[i]直接作为数组下标,即可完成更新,在其后的数并未添加到树里,所以求和的时候不会求到他。但在每次的更新中,我们其实更新了所有比他大的上级的数,所以在推到某数时,该数之前的num已经更新完毕。
有关lowbit:
求和:(遍历与他同层且小于他的数)
添加:(找祖宗)
这样我们就可以完成更新辣!
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define mod 998244353
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 5e5 + 50;
int arr[N];
int rk[N];//小于arr[k]的个数
int sum[N];//小于arr[k]的数和
int lowbit(int x) {
return x & (-x);
}
int qpow(int a, int b) {
a %= mod;
int ans = 1;
while (b > 0) {
if (b & 1)
ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int getnum1(int a) {
int num = 0;
while (a) {
num += rk[a];
num %= mod;
a -= lowbit(a);
}
return num;
}
void update1(int a) {
while (a < 4e5 + 50) {
rk[a]++;
rk[a] %= mod;
a += lowbit(a);
}
}
int getnum2(int a) {
int num = 0;
while (a) {
num += sum[a];
num %= mod;
a -= lowbit(a);
}
return num;
}
void update2(int a) {
int t = a;
while (a < 4e5 + 50) {
sum[a] += t;
sum[a] %= mod;
a += lowbit(a);
}
}
void work() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> arr[i];
}
int ans = 0;
for (int i = 1; i <= n; ++i) {
int cnt = getnum1(arr[i]) % mod;
update1(arr[i]);
int sum = (getnum2(4e5 + 50) - getnum2(arr[i]) + mod) % mod;
//小于4e5 + 50的所有数和-小于arr[i]的所有数和
update2(arr[i]);
ans = (ans + (cnt * 2 + 1) * arr[i] % mod + sum * 2 % mod) % mod;
cout << (ans * qpow(i * i % mod, mod - 2) + mod ) % mod << '\n';
}
}
signed main() {
io;
work();
return 0;
}
I.Problem - 1119A - Codeforces
思路:
找最后一个和1不一样的数和第一个和n不一样的数即可。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f
const int N = 3e5 + 10;
int arr[N];
void work() {
int n;
cin >> n;
int num = 0;
for (int i = 1; i <= n; ++i) {
cin >> arr[i];
}
for (int i = n; i >= 2; --i) {
if (arr[i] != arr[1]) {
num = max(num, i - 1);
break;
}
}
for (int i = 1; i < n; ++i) {
if (arr[i] != arr[n]) {
num = max(num, n - i);
break;
}
}
cout << num << '\n';
}
int main() {
io;
work();
}
K.Problem - D - Codeforces
思路:
二分答案+前缀和贪心。
排序数组,二分最大中位数。
check过程:令原数组中小于x的数都为-1,大于x的数都为1,这样每个数的贡献都是1,可以直接计数,前缀和就代表前缀中在中位数前后的数的个数差(*),从第k项开始一边判断一边贪心(可以不要的)前缀,可得check。
(*):当字串个数为奇时,中位数是中间的数,在上述计算中为+1,故该串总和为1;当为偶数时,题目要求中位数是第k/2大数,在上述计算中为+1,故该串总和为2,可证明判断的成立条件。
另:注意二分的边界。最后输出l-1的原因是:假定答案是某一步的mid,此时l变为mid+1,之后的check永远不会成功,直到r在某次减小到mid,无法进入循环,此时答案为l-1;假定答案是某一步的mid-1,此时r变为mid-1,之后的check永远不会失败,直到l在某次增加为mid,此时答案为l-1;假定答案是某一步的mid+1,此时l变为mid+1,仅在l==r的循环中check会成功一次使l变为l+1跳出循环,此时答案仍为l-1。
(之前学的时候同桌就想了很久,好久不写二分答案我刚刚也想了一会儿,浅推一手,这里当个备忘录。)
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
//#define mod 998244353
#define inf 0x3f3f3f3f
const int N = 1e6 + 5;
ll sum[N], arr[N], bb[N];
int n, k;
bool check(int x) {
mem(sum, 0);
for (int i = 1; i <= n; ++i) {
if (bb[i] >= x) {
//这里注意!一定是原数组和x去比较而非排序后的数组
sum[i] = sum[i - 1] + 1;
} else
sum[i] = sum[i - 1] - 1;
}
int mi = 0;
for (int i = k; i <= n; ++i) {
if (sum[i] - mi > 0)
return 1;
mi = min(mi, sum[i - k + 1]);
//贪心,sum[i] - mi尽量大即mi尽量小
}
return 0;
}
void work() {
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> arr[i];
bb[i] = arr[i];
}
sort(arr + 1, arr + n + 1);
int l = 1, r = n;
while (r >= l) {
int mid = (l + r) / 2;
if (check(arr[mid])) {
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << arr[l - 1] << '\n';//注意二分端点
}
signed main() {
io;
work();
return 0;
}
L.D - Dance (atcoder.jp)
思路:
回溯dfs爆搜,写dfs需要很细心
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int arr[20][20];
bool vis[N];
int ans = -1;
int n;
void dfs(int cur, int val) {
//说明在2*n之前的所有数都已经配对了达到要求
if (cur >= 2 * n) {
ans = max(ans, val);
return;
}
//说明在搜到cur之前他已经被配对了
if (vis[cur]) {
dfs(cur + 1, val);
return;
}
//遍历cur之后的所有元素和cur配对,分别去搜cur+1
for (int i = cur + 1; i <= 2 * n; ++i) {
if (!vis[i]) {
vis[i] = 1;
dfs(cur + 1, val ^ arr[cur][i]);
vis[i] = 0;
}
}
}
void work() {
cin >> n;
for (int i = 1; i < 2 * n; ++i) {
for (int j = i + 1; j <= 2 * n; ++j) {
cin >> arr[i][j];
}
}
dfs(1, 0);
cout << ans << '\n';
}
signed main() {
io;
work();
return 0;
}
M.SPOJ.com - Problem DCEPC206
思路:
会F了这题不是一眼切?
但是树状数组卡常我是真的蚌埠住了(卡lowbit?),不知道线段树会不会好一些(线段树建树似乎比树状数组快不少,而且我怀疑以这个卡法线段树才是正解)
一些位运算buff:
a-=lowbit(a) ==> a = (a & (a + 1)) - 1)
a+=lowbit(a) ==> a = (a | (a + 1))
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
//#define mod 998244353
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 1e6 + 5;
ll sum[N];//小于arr[k]的数和
ll num;
inline int lowbit(int x) {
return x & (-x);
}
inline ll getnum2(int a) {
num = 0;
for (; a >= 0; a = (a & (a + 1)) - 1) {
num += sum[a];
}
return num;
}
inline void update2(int a, int t) {
for (; a < N; a = (a | (a + 1))) {
sum[a] += t;
}
}
int main() {
//io;
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
mem(sum, 0);
ll ans = 0;
for (int i = 1; i <= n; ++i) {
int a;
scanf("%d", &a);
update2(a, a);
ans += getnum2(a - 1);
}
printf("%lld\n", ans);
}
return 0;
}
未完待续:航空管制
PS:感觉我的思维快要枯竭了怎么办