cf gym/103821 (Aleppo + HAIST + SVU + Private) CPC 2022

  • Post author:
  • Post category:其他


A

Laser Tag

题解:可以发现,我们此时的操作是将一段区间(不含端点)假如有数的话,就将其整体删除并在左右端点处加上1,根据我们学习的数据结构,其中有一个比较符合这种操作:珂朵莉树,也叫老司机树,他对于这种问题的做法是利用set,将该段区间整体从原来的地方划分出来,并将其删除,所以我们参考这种做法,将其删除:

#include<iostream>

#include<set>

#include<algorithm>

using namespace std;

#define int long long

const int maxn = 2e5 + 10;

struct node

{


int l, r, y;

inline bool operator<(const node&a)

{


return y < a.y;

}

}e[maxn];

int n, m;

void solve()

{


cin >> n >> m;

for (int i = 0; i < m; i++)

cin >> e[i].l >> e[i].r >> e[i].y;

sort(e, e + m);

set<int>s;//应该可以像obt那样开结构体来处理,不过直接int也可以

for (int i = 1; i <= m; i++)

s.insert(i);

for (int i = 0; i < m; i++)

{


set<int>::iterator it = s.lower_bound(e[i].l);

if (it != s.end() && *it <= e[i].r)//存在时

{


set<int>::iterator it2= s.lower_bound(e[i].r);

s.erase(it, it2);

s.insert(e[i].l);

s.insert(e[i].r);

}

if (!s.size())//避免加下来查找不到

break;

}

cout << s.size() << endl;//将size直接当成答案

}

signed main()

{


int t;

cin >> t;

while (t–)

solve();

}

B:题目并没有要求我们要最小次数的翻转,所以我们先暴力将其全部翻转一次,看看能不能出现答案,假如并没有的话,就输出-1,此时翻转次数是有上限的,不太会证明,同时对于结果,我们假如翻转了奇数次,实际就为1次,偶数次就为0次

/*

题目并没有要求我们要最小次数的翻转,所以我们先暴力将其全部翻转一次,看看能不能出现答案,假如并没有的话,就输出-1,

此时翻转次数是有上限的,不太会证明,同时对于结果,我们假如翻转了奇数次,实际就为1次,偶数次就为0次

*/

#include<iostream>

#include<set>

#include<algorithm>

using namespace std;

#define int long long

const int maxn = 2e2+10;

int n, m;

int a[maxn][maxn];

bool con[maxn], row[maxn];//代表此时行列是正是负

int ansr[maxn], ansc[maxn];//代表行列翻转次数

int flag = 0;//是否为正

bool check()//判断此时是否合法

{


for (int i = 1; i <= n; i++)

{


int sum = 0;

for (int j = 1; j <= m; j++)

sum += a[i][j];

if (sum < 0)

return 0;//代表不合法

}

for (int i = 1; i <= m; i++) //同理

{


int sum = 0;

for (int j = 1; j <= n; j++)

sum += a[j][i];

if (sum < 0)

return 0;

}

return 1;

}

void work()//进行翻转

{


for (int i = 1; i <= n; i++)row[i] = 0;

for (int i = 1; i <= m; i++)con[i] = 0;

for (int i = 1; i <= n; i++)

{


int sum = 0;

for (int j = 1; j <= m; j++)

sum += a[i][j];

if (sum < 0)

row[i] = 1;//代表此时要翻转

}

for (int i = 1; i <= n; i++)

{


if (row[i])

{


ansr[i]++;

for (int j = 1; j <= m; j++)

a[i][j] = -a[i][j];

row[i] = 0;

}

}

for (int i = 1; i <= m; i++)

{


int sum = 0;

for (int j = 1; j <= n; j++)

sum += a[j][i];

if (sum < 0)

con[i] = 1;

}

for (int i = 1; i <= m; i++)

{


if (con[i])

{


ansc[i]++;

for (int j = 1; j <= n; j++)

a[j][i] = -a[j][i];

con[i] = 0;

}

}

if (check())

{


flag = 1;

return;

}

}

void solve()

{


cin >> n >> m;

flag = 0;

for (int i = 1; i <= n; i++)

for (int j = 1; j <= m; j++)cin >> a[i][j];

int tim = 1000;

while (tim–)

{


work();

if (flag == 1)

break;

}

if (!check()){


cout << “No” << endl;

}

else

{


cout << “Yes” << endl;

int k = 0;

for (int i = 1; i <= n; i++)

if (ansr[i] % 2)

k++;

cout << k << endl;

for (int i = 1; i <= n; i++)

{


if (ansr[i] % 2)

cout << i << ‘ ‘;

ansr[i] = 0;

}

cout << endl;

k = 0;

for (int i = 1; i <= m; i++)

if (ansc[i] % 2)

k++;

cout << k << endl;

for (int i = 1; i <= m; i++)

{


if (ansc[i] % 2)

cout << i << ‘ ‘;

ansc[i] = 0;

}

cout << endl;

}

}

signed main()

{


int t;

cin >> t;

while (t–)

solve();

}

C:签到的不能再签到

D:队友写的,也是比较签到的一题

E:要注意到范围限制,他最多有1e9,不能直接模拟,然后根据答案推一下结果就好了

F:签到的不能再签到

G:不会

H:比较好推的是根据组合数来推,当同时我们要注意,他的n是没有规定sum的,即要是一个一个求得话复杂度为:0(nT),所以我们必须找其他得路,通过对几组数据得模拟:利用组合数,不难发现其规律为:

ans[i]=ans[i-1]+ans[i-2]+1;

所以答案就出来了

I:不会

J:等下再补

K:

我是利用线段树来处理得,这题实验室每个人都有不同做法,不过我比赛是一个+=写为=,没发现一直错

我们对每个区间进行询问,找其他区间和他在一起的合法解,可以发现,要是一个区间和他是合法的话,必须保证后一个区间的l要大于前一个区间的r,同时他的贡献是由后一个区间的末尾到M距离*前一个区间的左端点到1的距离,所以我每次都将该贡献放在了区间左端点的上,在进行询问:

#include<iostream>

#include<cstring>

#include<cmath>

#include<algorithm>

using namespace std;

#define int long long

const int maxn = 2e5 + 10;

const int mod = 1e9 + 7;

struct node {


int l, r;

int val;

}tree[maxn << 2];

struct node1 {


int l, r;

inline bool operator<(const node1&p)const {


return l < p.l;

}

}e[maxn];

int a[maxn];

int q_mul(int a, int b)

{


a %= mod, b %= mod;

int ans = 0;

while (b)

{


if (b & 1)

{


ans = (ans + a) % mod;

}

b >>= 1;

a = (a + a) % mod;

}

return ans % mod;

}

void pushdown(int root)

{


tree[root].val = (tree[root << 1].val + tree[root << 1 | 1].val) % mod;

}

void build(int root, int l, int r){


tree[root].l = l, tree[root].r = r;

if (l == r)

{


tree[root].val = a[l] % mod;

return;

}

int mid = l + r >> 1;

build(root << 1, l, mid);

build(root << 1 | 1, mid + 1, r);

pushdown(root);

}

int query(int root, int ql, int qr){


int mid = (tree[root].l + tree[root].r) >> 1;

if (ql == tree[root].l&&qr == tree[root].r)

{


return tree[root].val%mod;

}

if (qr <= mid)

return query(root << 1, ql, qr);

else if (ql > mid)

return query(root << 1 | 1, ql, qr);

else

{


int ret = query(root << 1, ql, mid) % mod;

ret += query(root << 1 | 1, mid + 1, qr);

return ret % mod;

}

}

void solve(){


int n, m;

cin >> n >> m;

memset(a, 0, sizeof a);

for (int i = 1; i <= n; i++) {


cin >> e[i].l >> e[i].r;

a[e[i].l]+=(m – e[i].r + 1);

}

sort(e + 1, e + 1 + n);

build(1, 1, m);

int ans = 0;

for (int i = 1; i <= n; i++) {


if (e[i].r + 1 <= m)

ans = (mod + q_mul(query(1, e[i].r + 1, m), e[i].l) + ans) % mod;

}

cout << ans << ‘\n’;

}

signed main(){


ios::sync_with_stdio(false);

cin.tie(0), cout.tie(0);

int t;

cin >> t;

while (t–)

solve();

}

L:他有一个连续没看见,读了假题,换了好几种写法都没过,导致心态差点崩了(还是太菜,明年再争取icpc名额)

具体就是暴力就好了:

#include<iostream>

#include<algorithm>

using namespace std;

#define int long long

const int maxn = 1e3 + 10;

int a[maxn];

int b[maxn];

int n, k;

bool cmp(int a, int b)

{


return a > b;

}

void solve(){


cin >> n >> k;

for (int i = 1; i <= n; i++)

cin >> b[i];

int ans = 0;

int now = 1;

int cnt = 1;

a[1] = b[1];

if (b[n] % 2)

b[n + 1] = 0;

else

b[n + 1] = 1;

for (int i = 2; i <= n+1; i++){


if (b[i] % 2 == b[i – 1] % 2)

{


a[++cnt] = b[i];

}

else{

if (cnt <= k){


for (int j = 1; j <= cnt; j++){


ans += a[j];

}

}

else{


sort(a + 1, a + 1 + cnt, cmp);

for (int j = 1; j <= k; j++)

ans += a[j];

}

cnt = 1;

a[1] = b[i];

}

}

cout << ans << endl;

}

signed main()

{


int t;

cin >> t;

while (t–)

solve();

}

M:我们具体做法就是:首先可以发现,对于每一个合法对数都会在任意排序中出现一次,不过就是先后问题,比如:1 4 这两数会在任意序列中都出现,不过一半是1 4,一半是 4 1,这就比较好确定一点对于任意有意义的,在全排列中仅有一半是合法的:

#include<iostream>

#include<algorithm>

using namespace std;

#define int long long

const int maxn = 1e5 + 20;

int f[maxn];

int inv[maxn];

int s[maxn];

const int mod = 1e9 + 7;

signed main()

{


for (int i = 1; i <= 100010; i++)

{


for (int j = 2; j*i <= 100100; j++)

f[i*j]++;//统计合法的

}

for (int i = 1; i <= 100010; i++)

s[i] = (s[i – 1] + f[i])%mod;//s[i]代表1-i中所有数合法的请款

inv[2] = 1;//直接在阶乘中/2

for (int i = 3; i < 100010; i++)

inv[i] = inv[i – 1] *i%mod;

int t;

cin >> t;

while (t–)

{


int n;

cin >> n;

if (n == 1)

{


cout << 0 << endl;

continue;

}

cout << inv[n] * s[n] % mod << endl;

}

}

总结:太菜了



版权声明:本文为qq_62105443原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。