SZUACM2022招新积分赛Day1 总结
SZUACM2022招新积分赛 Day1
题目
下午场:
晚上场:
感想
下午场的状态实在差到离谱。。而且我图论完全乱来的emmm练得太少了(深刻忏悔)
感觉下午场难好多,搞得我非常的自闭。。。
晚上感觉还好一点,唉。。我好菜
下午场就基本是思路都没有,晚上场还可以说是有些差一点(漏情况)(虽说也很不应该。。。
补题
下午场
没做出来的有点多,明天上午再看看。。
A. Gym 103687A
考虑奇偶性,步数的判断。我出错的点就在于,没想到答案不会超过3,一步一步凑的。。。好笨
具体的看注释后面的样例
#include <bits/stdc++.h>
using namespace std;
int main () {
int t;
cin >> t;
while (t --) {
int a, b;
cin >> a >> b;
if (a > b) {
if ((a - b) % 2 == 0)
cout << 1 << endl;
else
cout << 2 << endl;
}
else if (a == b)
cout << 0 << endl;
else {
if ((b - a) % 2)
cout << 1 << endl;
else if ((b - a) / 2 % 2)
cout << 2 << endl;
else
cout << 3 << endl;
}
}
}
//改变奇偶性
//eg.2->6: +3 +3 -2
//答案不会超过3
B. Gym 103145K
改了很久很久的
TLE的原因就在于少了一个优化,记录上一次遍历到了哪条边
(以及,排序排的不行,记错了对的默认顺序,默认大根堆)
//逆向思考, 删边就转化为不加入边
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair <int, int> pii;
const int N = 2e5 + 5;
int n, m, k;
int fa[N], sz[N]; //节点个数
int sum;
struct Node {
int a, b, w;
bool operator < (const Node & t) const {
return w < t.w;
}
}e[N];
priority_queue <pii> q; //fi值, se下标 //大根堆
int ans[N];
void init () {
for (int i = 1; i <= n; i ++)
fa[i] = i, sz[i] = 1;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
void solve () {
sum = 0;
scanf("%lld%lld%lld",&n,&m,&k);
for (int i = 1; i <= m; i ++)
scanf("%lld%lld%lld",&e[i].a,&e[i].b,&e[i].w);
sort (e + 1, e + m + 1);
// for (int i = 1; i <= m; i ++)
// cout << e[i].w << ' ';
// cout << endl;
for (int i = 1; i <= k; i ++) {
int x;
scanf("%lld",&x);
q.push ({x, i});
}
init ();
int last = m;
while (!q.empty ()) {
auto t = q.top();
q.pop();
int v = t.first;
//优化: 记录上一次遍历到了哪条边
int i;
for (i = last; i >= 1; i --) {
if (e[i].w < v) {
break;
}
int a = find (e[i].a), b = find (e[i].b);
if (a != b) {
sum += sz[a] * sz[b];
fa[a] = b;
sz[b] += sz[a]; //notice:反过来
}
}
last = i;
ans[t.second] = sum;
}
for (int i = 1; i <= k; i ++)
printf("%lld\n",ans[i]);
}
signed main () {
int t; scanf("%lld",&t);
while (t --) {
solve ();
}
}
//离线处理
//>=k就加边
C. HDU - 5656
预处理gcd+dp枚举各种可能
//预处理之玄妙
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005, mod = 100000007;
int n;
int f[N][N]; //i个数的所有组合中gcd为j的个数
int g[N][N]; //gcd(i,j)
int a[N];
void solve () {
memset (f, 0, sizeof f);
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> a[i];
//枚举各种可能
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= 1000; j ++) {
f[i][j] = (f[i - 1][j] + f[i][j]) % mod; //不取
f[i][g[a[i]][j]] = (f[i - 1][j] + f[i][g[a[i]][j]]) % mod; //取
}
f[i][a[i]] ++; //gcd(a[i], a[i]) = a[i];
}
ll ans = 0;
for (int i =1; i <= 1000; i ++)
ans = (ans + 1ll * i * f[n][i] % mod) % mod;
cout << ans << endl;
}
int main () {
//预处理
for (int i = 1; i <= 1000; i ++)
for (int j = 1; j <= 1000; j ++)
g[i][j] = g[j][i] = __gcd (i, j);
int t; cin >> t;
while (t --) {
solve ();
}
}
//求所有gcd之和
//预处理存答案
//把gcd(i,j)预处理,存进g[i][j],g[j][i]
//dp
//f[i][j]:前i个数的所有组合中gcd为j的个数
晚上场
A. HDU - 5723
第一问Kruscal最小生成树,
第二问不会,看了题解说是树形dp: 计算任意两点间平均距离,即计算任意两点间的距离和,然后除以 \(\frac{n(n-1)}{2}\) 即可。
第二问不知道为啥还没调出来
//Kruscal + 树形dp(算任意两点之间的平均期望: dfs求距离和 / C_n^2)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, m;
int fa[N];
bool vis[N];
int res;
struct Node {
int a, b, w;
bool operator < (const Node &t) const {
return w < t.w;
}
}e[N << 1];
struct edge {
int a, b;
edge (int a_, int b_) : a(a_), b(b_) {}
};
vector <edge> g[N]; //求任意两点间距离
void init () {
for (int i = 1; i <= n; i ++)
fa[i] = i;
}
int find (int x) {
if (fa[x] != x)
fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
sort (e, e + m);
init ();
int ans = 0, cnt = 0;
for (int i = 0; i < m; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
cnt ++;
if (cnt == n - 1)
break;
}
}
return ans;
}
int dfs (int u) {
vis[u] = true;
int cnt = 1;
for (int i = 0; i < g[u].size (); i ++) {
auto x = g[u][i];
if (vis[x.a]) continue;
int tmp = dfs (x.a);
res += tmp * (n - tmp) * x.b;
cnt += tmp;
}
return cnt;
}
int main () {
int t; cin >> t;
while (t --) {
cin >> n >> m;
for (int i = 1; i <= m; i ++)
cin >> e[i].a >> e[i].b >> e[i].w;
cout << kruscal () << ' ' ;
memset (vis, false, sizeof vis);
res = 0;
dfs (1);
//cout << res << endl;
double sum = (res * 1.0) / (1.0 * n * (n - 1) / 2);
printf ("%.2lf\n", sum);
for (int i = 1; i <= n; i ++)
g[i].clear ();
}
}
B. Gym 103107K
我贪错心了,直接想假了emmm
其实直接全部拼在一起就好
#include <bits/stdc++.h>
#define int long long
using namespace std;
//priority_queue <int, vector <int>, greater <int>> q;
priority_queue <int> q;
int ans = 0;
int a, b;
int maxn= 0;
signed main () {
int n, k;
cin >> n >> k;
while (n --) {
int x;
cin >> x;
ans += x;
}
if (ans >= k)
cout << ans - (k + 1) / 2; //ceil
else
cout << 0;
}
//害,想假了
C. Gym 103107J
错因:一对一匹配,所以最终B没有的话也不行(因B的匹配也要看)
双向考虑
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 5e5 + 5;
int a[N], b[N];
int ans1, ans2;
int main () {
int n, m;
scanf("%d%d",&n,&m);
ans1 = ans2 = n;
while (m --) {
int x, y;
scanf("%d%d",&x,&y);
a[x] ++, b[y] ++; //一对一匹配,所以也要考虑b啊
if (a[x] >= n)
ans1 --;
if (b[y] >= n)
ans2 --;
}
cout << min (ans1, ans2);
}
//有多少个A可以正常跑的
//A与所有的B都不能跑
//配对
//一一匹配所以B也要考虑
//卡读写的题就是纯纯的。。。
D.CodeForces 1628B
差一点就能写出div1B了(少讨论了前3后2的情况)
(话说我其实想到了,但是没写上去,不知道为啥,晕了)
Way1:在set里面找,似乎要慢一点
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
void solve () {
set<string> s;
int n;
cin >> n;
bool flag = false;
for (int i = 1; i <= n; i ++) {
string t;
cin >> t;
if (flag)
continue;
if (t[0] == t[t.size() - 1]) {
flag = true;
continue;
} //自身回文
s.insert (t);
int len = t.size ();
reverse (t.begin(), t.end());
if (s.count (t)) {
flag = true;
continue;
}
//ab cba
//t="abc"
//现3,匹配2
if (len == 3) {
string tt;
for (int i = 0; i < 2; i ++)
tt += t[i];
//cout << tt << endl;
if (s.count (tt)) {
flag = true;
continue;
}
}
//现2,匹配3
else if (len == 2) {
for (auto j : s) {
if (t[0] == j[0] && t[1] == j[1]) {
flag = true;
break;
}
}
}
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
int main () {
int t;
cin >> t;
while (t --) {
solve ();
}
}
//去掉某几个后能否变得回文
//如果找到两个完全相反的一定yes
//出现单个回文yes
//中间多出来一个也yes(case4)
//eg
//ab (cc) ba
//NO
//NO
//a
//ab cba
//NO
Way2:直接枚举26种拼接(与方法一就只有前3后2的判别上有区别)
比第一种快一些(但是大佬觉得太暴力了,打灭x)
//加字母拼起来,更快
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
void solve () {
set<string> s;
int n;
cin >> n;
bool flag = false;
for (int i = 1; i <= n; i ++) {
string t;
cin >> t;
if (flag)
continue;
if (t[0] == t[t.size() - 1]) {
flag = true;
continue;
} //自身回文
s.insert (t);
int len = t.size ();
reverse (t.begin(), t.end());
if (s.count (t)) {
flag = true;
continue;
}
//ab cba
//t="abc"
//现3,匹配2
if (len == 3) {
string tt;
for (int i = 0; i < 2; i ++)
tt += t[i];
//cout << tt << endl;
if (s.count (tt)) {
flag = true;
continue;
}
}
//现2,匹配3
else if (len == 2) {
//更快的做法
for (char ch = 'a'; ch <= 'z'; ch ++) {
string tmp = t + ch;
if (s.count (tmp)) {
flag = true;
break;
}
}
// for (auto j : s) {
// if (t[0] == j[0] && t[1] == j[1]) {
// flag = true;
// break;
// }
// }
}
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
int main () {
int t;
cin >> t;
while (t --) {
solve ();
}
}
//去掉某几个后能否变得回文
//如果找到两个完全相反的一定yes
//出现单个回文yes
//中间多出来一个也yes(case4)
//eg
//ab (cc) ba
//NO
//NO
//a
//ab cba
//NO
E.CodeForces 597C
BIT + dp
主要是用没想到可以用树状数组来求和
dp倒是想到了
记得开long long
//dp + BIT
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5;
int a[N], ans;
int n, k;
int f[11][N]; //长度 结尾
int lowbit (int x) {
return x & (-x);
}
int ask (int *f, int x) {
int res = 0;
while (x > 0) {
res += f[x];
x -= lowbit (x);
}
return res;
}
void update (int *f, int x, int val) {
while (x <= n) {
f[x] += val;
x += lowbit (x);
}
}
signed main () {
cin >> n >> k;
for (int i = 1; i <= n; i ++)
cin >> a[i];
//每一个都可
if (k == 0) {
cout << n << endl;
return 0;
}
k -= 1;
for (int i = 1; i <= n; i ++) {
ans += ask (f[k], a[i]);
for (int j = k; j > 0; j --)
update (f[j], a[i], ask (f[j - 1], a[i]));
update (f[0], a[i], 1);
}
cout << ans << endl;
}
//dp
//长度为k + 1的上升子序列的数量
//长度对应数量:3^{n - 1} + 1, n=f[i] - k - 1
//1 + 3 + 3
//但是这样没法除去重复的
//再加一维记录落脚点
//落脚点为ai才统计
//算了。。整不出来
F. CodeForces 664A
没想到防ak的题目才是真正的签到题(真是极具迷惑性)
直接赌徒心态
#include <bits/stdc++.h>
using namespace std;
int main () {
string s, t;
cin >> s >> t;
if (s == t)
cout << s;
else {
cout << 1;
}
}
//赌
其余的题目明早补。
希望明天我能给力点啊QAQwwwww