Codeforces Round #510 (Div. 2)
可能我太就没参加比赛了..这次比赛思路出的蛮快的,但是emmmm代码能力下降了。
大概是...在洛谷刷的水题太多了TAT
目前简单把自己看的题目写出来,之后继续补题。
前三道题:第一、三题都是模拟,第二道我跑了个DFS,有空看一下题解的解法。
这里提前讲一下第二题我是DFS写完之后没考虑第一步就完成操作的...得到所有解法都是一步以上的..
A. Benches
题意:沙滩上有n个位置,且有n个人要来,问位置上最多的人的最大可能和最小可能
最大可能很简单,就是都往原来最多的位置走。
最小我是先把小于最大值的填满,填不满就是最大值,填的满就继续平均分,求最大
#include<string>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#define FOR(a,b) for(register int i=a;i<=b;i++)
#define ULL unsigned long long
#define LL long long
using namespace std;
int main()
{
int a[10000];
int n,m;
cin >> n>>m;
int Mi,Mx;
int sum = 0;
FOR(1, n)
{
cin >> a[i];
sum += a[i];
}
sort(a + 1, a + 1 + n);
Mx = a[n] + m;
int M = a[n];
sum = -sum + M * n;
if (sum >= m)Mi = M;
else
{
if((m-sum)%n)
Mi = M + (m - sum) / n + 1;
else Mi = M + (m - sum) / n;
}
cout << Mi << " " << Mx << endl;
return 0;
}
B. Vitamins
题意:有n个交易,给多少钱,还你ABC,问拿到ABC所有种类需要多少钱
DFS跑所有路径,如果接下来的点对你的ABC种类没有贡献就不跑。
PS:我已开始漏判断了一个交易就直接满足且最优的情况 FST了
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#define FOR(a,b) for(register int i=a;i<=b;i++)
#define ULL unsigned long long
#define LL long long
using namespace std;
int F[4];
struct node
{
int p;
char s[5];
}A[3000];
void cal(char s[])
{
memset(F, 0, sizeof(F));
int sum = 0;
for (int i = 0; i < strlen(s); i++)
{
F[s[i] - 'A' + 1] = 1;
}
}
int cmp(node a, node b)
{
return a.p < b.p;
}
int H[10000000];
int cnt = 0;
int E[1010];
int flag = 1;
void dfs(int a, int b, int c, int sum, int n)
{
if (a == 1 && b == 1 && c == 1)
{
H[++cnt] = sum;
// cout << sum << ":";
//for (int i = 1; i <= n; i++)
//cout << E[i] << " ";
//cout << endl;
return;
}
else
{
for (int i = 1; i <= n; i++)
{
if (!E[i])
{
flag = 0;
cal(A[i].s);
// cout << F[1] << " " << F[2] << " " << F[3] << endl;
// cout << a << " " << b << " " << c << " " << endl;
if (a == 0 && F[1])flag = 1;
if (b == 0 && F[2])flag = 1;
if (c == 0 && F[3])flag = 1;
if (flag)
{
E[i] = 1;
dfs(F[1] || a, F[2] || b, F[3] || c, A[i].p + sum, n);
E[i] = 0;
}
}
}
}
}
int main()
{
memset(E, 0, sizeof(E));
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> A[i].p;
cin >> A[i].s;
cal(A[i].s);
if (F[1] && F[2] && F[3])
{
H[++cnt] = A[i].p;
}
}
dfs(0, 0, 0, 0, n);
if (cnt)
{
sort(H + 1, H + 1 + cnt);
cout << H[1] << endl;
}
else cout << "-1" << endl;
//return H[1];
}
C. Array Product
题意:两个操作:
A.把i,j变成一个,且a[i]消失,a[j]=a[i]*a[j]
B.直接删除一个元素,但只能使用一次
问怎么操作,使得操作到只剩下一个元素的时候值最大。
解法写在注释里了
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<cctype>
#define rep(a,b) for(register int i=a;i<=b;i++)
#define red(a,b) for(register int i=a;i>=b;i--)
#define ULL unsigned long long
#define LL long long
using namespace std;
inline int read();
inline double dbread();
vector<LL>vec0;
vector<LL>vecF;
vector<LL>vecA;
LL arr[200050];
int main()
{
int n;
LL MAX=10e11+7;
int MI = -1;
cin >> n;
rep(1, n)
{
scanf_s("%I64d", &arr[i]);
if (!arr[i])vec0.push_back(i);
if (arr[i] < 0)
{
vecF.push_back(i);
if (MAX > abs(arr[i]))
{
MAX = -arr[i];
MI = i;
}
}
}
rep(1, n)
{
if (i != MI&&arr[i]!=0)
{
vecA.push_back(i);
}
}
//特殊情况处理:最后为0的答案必须要留下0,不能清除。
/*
总体思想:
非零数相乘为正,直接写出答案,消除0即可;
为负,需要用一个0消去绝对值最小的负数或者自我消除。
在之前把所有0消去即可。
所以预处理判断答案是否为0,如果为0,即只有一个负数或者没有非零数。
*/
if (vecA.size() == 0)//只有一个负数也会导致没有元素
{
if(vec0.size()>1)
rep(0, vec0.size()-1-1)
{
printf("1 %I64d %I64d\n", vec0[i], vec0[i + 1]);
}
if(MI!=-1)
printf("1 %I64d %d\n",vec0[vec0.size()-1],MI);
}
else
{
if (vecF.size() % 2)
{
if (vec0.size() > 0)
printf("1 %Id %I64d\n", MI, vec0[0]);
else
printf("2 %d\n", MI);
}
else if(MI!=-1) vecA.push_back(MI);
//cout << vec0.size();
if(vec0.size()>1)
rep(0,vec0.size()-1 -1)
printf("1 %I64d %I64d\n", vec0[i],vec0[i+1]);
if(vec0.size()>0)
printf("2 %I64d\n",vec0[vec0.size()-1 ]);
if(vecA.size()>1)
rep(0, vecA.size() - 1 - 1)
printf("1 %I64d %I64d\n", vecA[i], vecA[i + 1]);
}
//system("pause");
return 0;
}
D.Petya and Array
题意:中途几天为了做这题还跑去看树状数组,等会做了才发现线段树也可以做QAQ.就是逆序对的一道变题,需要离散化的操作QAQ明明我之前研究过的。果然还是洛谷水题做多了。
sum[j]-sum[i-1]<k
sum[j]<sum[i-1]+k
就是求逆序对,对于每加入一个sum[j],判断之前是否放入过比他大的sum[i-1]+k。
权值树状数组?
数据较大,离散化操作即可,因为数据总大小只有2*10e5
把数据离散化成:1 2 3 4 5 6 6 6 7 8这样子,利用权值线段树即可,我这里用的是树状数组
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
int sum[200050*2];
int lowbit(int x)
{
return x & (-x);
} //返回最小的值
void update(int pos, int val,int x)
{
while (pos <= x)
{
sum[pos] += val;
pos += lowbit(pos);
}
}//维护的是前缀和
int query(int pos)
{
int s = 0;
while (pos)
{
s += sum[pos];
pos -= lowbit(pos);
}
return s;
}//就算是前面的更新,也只是把需要用到的更新了,求区间和的时候需要按1的位置加起来。
struct node
{
int id;
long long w;
}arr[200050*2];
int B[200050*2];
int cmp(node a, node b)
{
return a.w < b.w;
}
int main()
{
int N;
long long t;
cin >> N >> t;
int cnt1 = 0, cnt2 = 0;
int d = 0;
memset(arr, 0, sizeof(arr));
for (int i = 1; i <= N; i++)
{
scanf("%lld", &arr[i].w);
arr[i].w += arr[i - 1].w;
arr[i].id = i;
}
for (int i = N + 1; i <= 2 * N; i++)
{
arr[i].w = arr[i - N - 1].w + t;
arr[i].id = i;
}
sort(arr + 1, arr + 1 + 2*N, cmp);
for (int i = 1; i <= 2 * N; i++)
{
if(arr[i].w!=arr[i-1].w)
B[arr[i].id] = ++d;
else B[arr[i].id] = d;
// cout << arr[i].w << " ";
}
//cout << endl;
//cout << d << endl;
long long ans = 0;
for (int i = 1; i <= N; i++)
{
update(B[i+N], 1,d);
//cout << B[i + N]<<endl;
ans += query(d) - query(B[i]);
//cout << B[i] << endl;
//cout << ans << endl;
}
cout << ans << endl;
return 0;
}
E. Vasya and Magic Matrix
题意:给定一个芯片,芯片可以往比自己所在位置权值低的位置走,走过去的贡献是曼哈顿距离的平方,如果能走,走的操作是随机发生的。求贡献的期望。
期望:正着推很难,从最终位置反向推就会简单些:
继续化简即可。
j为到达a[i]之前所在的位置,权值显然比a[i]的大,排序,递推即可。
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<cctype>
#define rep(a,b) for(register int i=a;i<=b;i++)
#define red(a,b) for(register int i=a;i>=b;i--)
#define ULL unsigned long long
#define LL long long
#define MOD 998244353
using namespace std;
int r, c;
LL dp[3000][3000];
struct node
{
int x;
int y;
LL val;
}matrix[1000100];
LL quickpow(long long n, long long base) {
long long res = 1;
while (n) {
if (n & 1) {
res = res * base % MOD;
}
n >>= 1;
base = base * base % MOD;
}
return res;
}//快速幂
int inv(int a)
{
return quickpow(MOD-2,a);
}
int check(int n,int m)
{
for (int i = 1; i <= n * m; i++)
{
if (matrix[i].x == r && matrix[i].y == c)
{
return i;
}
}
}
int cmp(node a, node b)
{
return a.val < b.val;
}
int main()
{
int n, m;
cin >> n >> m;
int cnt = 0;
memset(matrix, 0, sizeof(matrix));
for(int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%lld", &matrix[++cnt].val);
matrix[cnt].x = i; matrix[cnt].y = j;
//cout << matrix[i].val << endl;
}
}
cin >> r >> c;
sort(matrix + 1, matrix + 1 + n * m,cmp);
// for (int i = 1; i <= n * m; i++)
// cout << matrix[i].val << endl;
int res=check(n, m);
LL totF = 0, totX = 0, totY = 0, tot = 0;
LL anF = 0, ansX = 0, ansY = 0;
LL ans = 0;
//cout << res << endl;
rep(1, res)
{
LL x = matrix[i].x, y = matrix[i].y;
//cout << ans << endl;
ans = (((x * x + y * y) % MOD*tot + anF -2 * x * ansX - 2 * y * ansY)%MOD + MOD) % MOD;
ans = (ans*inv(tot)) % MOD;
totF = (totF + ans + (x * x + y * y)) % MOD;
totX = (totX + x) % MOD;
totY = (totY + y) % MOD;
if (matrix[i].val != matrix[i + 1].val)
{
anF = totF % MOD;
ansX = totX % MOD;
ansY = totY % MOD;
tot = i;
}
}
printf("%I64d", ans);
//system("pause");
}
F. Leaf Sets
题意:在树中有多少个区域,区域内任意两点距离<=k。
见代码注释
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<cctype>
#define rep(a,b) for(register int i=a;i<=b;i++)
#define red(a,b) for(register int i=a;i>=b;i--)
#define ULL unsigned long long
#define LL long long
using namespace std;
const int N = 10e6 + 100;
vector< int >vec[N];
/*
对于一棵树,从中间节点开始遍历//PS:便于dfs判断到底,
遍历所到的每一个子树,筛掉大的,
大的往上只会更大,更不符合条件,
小的留下来递归回去+1判断是否符合条件,
符合就留下来
继续上去,不符合又筛掉
每次只对儿子处理
*/
int ans = 0, k;
int dfs(int v, int p)
{
if (vec[v].size() == 1)
{
return 0;
}
vector< int >val;
for (auto u : vec[v])
{
if (u == p)continue;
val.push_back(dfs(u, v) + 1);
}
sort(val.begin(), val.end());
while (val.size() >= 2)
{
if (val.back() + val[val.size() - 2] <= k)
break;
ans++;
val.pop_back();
}
return val.back();//子树最大距离:如果符合,一定是距子树底的最远距离也合格起码。
}
int main()
{
int n;
int a, b;
cin >> n >> k;
rep(1, n - 1)
{
scanf("%d%d", &a, &b);
a--, b--;
vec[a].push_back(b);
vec[b].push_back(a);
}
rep(0, n - 1)
{
if (vec[i].size() > 1)
{
dfs(i, -1);
break;
}
}
cout << ans + 1 << endl;//加上初始留到最后的那一个val。
// system("pause");
}