本来是当做准备校赛前的信心赛打的,结果还是有好几道看了题解才明白.....加油吧~
A A++++++++++++++++++
大意:
对于给出的n个title中,每能组成一对<national,international>,那么竞赛的等级就可以在A的基础上增加一个+,问最后的竞赛等级
思路:
取min即可
#include<iostream>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
string s;
int num1=0,num2=0;
for(int i=0;i<n;i++){
cin>>s;
if(s=="national") num1++;
if(s=="international") num2++;
}
cout<<"A";
for(int i=0;i<min(num1,num2);i++)
{
cout<<"+";
}
cout<<endl;
}
return 0;
}
B GDP Carry
大意:
给出n个数,Antinomy先手,需要取连续的一段和为奇的数,小西后手,需要取连续的一段和为偶的数,无法继续则判定为输。问在最佳策略下谁会赢
思路:
如果n个数的和为奇,那么Antinomy先手直接胜利,如果和为偶,且存在奇数,那么Antinomy可以先取一个奇数,使得剩下的和为奇数(偶数-奇数=奇数),此时小西由于只能选偶数,所以小西必然只能给Antinomy留下奇数,所以又转化为了Antinomy必赢的局面,如果和为偶且不存在奇数,那么小西必赢,因为Antinomy先手没有数可以取。
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
long long a[N];
int main(){
int n;
cin>>n;
int f=0;
long long sum=0;
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
if(a[i]%2) f++;
}
if(sum%2==0&&f==0) cout<<"XiJam"<<endl;
else cout<<"Antinomy"<<endl;
return 0;
}
C Interpretability
大意:
给出n个数字(f_i)代表(2^i)的数量,问由这些数最多能组成多少个三角形
思路:
由于都是(2^i),所以只可能是三个相同的数或者两个相同的数加一个较小的数。所以从小到大扫一遍,看将(f_i)拿出一对一对的情况下,和前面(包括自己)单个的边能组成三角形的数量,如果剩下了,就将其作为单个的边记录下来。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 4e5 + 10;
LL n, m, T, a[MAXN];
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
LL pair = 0, single = 0, res = 0;
LL tmp = 0, minv = 0;
for (int i = 1; i <= n; ++i) {
LL pair_cnt = a[i] / 2;
LL single_cnt = a[i] % 2;
tmp = pair_cnt;
single += single_cnt;
minv = min(pair_cnt, single);
res += minv;
LL cnt3 = (pair_cnt - minv) * 2 / 3;
res += cnt3;
single -= minv, single += (pair_cnt - minv) * 2 % 3;
}
cout << res;
return 0;
}
D Batch Normalization 1D
大意:
模拟题
思路:
大模拟题,不想做了,piao的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 50;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
LL powmod(LL a, LL b) { LL res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; }
int n, c, n_batch;
double eps, momentum;
double X[N][N];
double Gamma[N];
double Beta[N];
double moving_mean[N];
double moving_var[N];
double mu[N];
double var[N];
double var_tmp[N][N];
double X_hat[N][N];
double out[N][N];
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> c >> n_batch >> eps >> momentum;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= c; j++) {
cin >> X[i][j];
}
}
for (int i = 1; i <= c; i++) {
cin >> Gamma[i];
}
for (int i = 1; i <= c; i++) {
cin >> Beta[i];
}
for (int i = 1; i <= c; i++) {
cin >> moving_mean[i];
}
for (int i = 1; i <= c; i++) {
cin >> moving_var[i];
}
for (int i = 1; i <= c; i++) { //cal mu
double sum = 0.0;
for (int j = 1; j <= n; j++) {
sum += X[j][i]; //order!
}
mu[i] = sum / n;
}
//cal var
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= c; j++){
var_tmp[i][j] = pow((X[i][j] - mu[j]), 2);
}
}
for (int i = 1; i <= c; i++) {
double sum = 0.0;
for (int j = 1; j <= n; j++) {
sum += var_tmp[j][i]; //order!
}
var[i] = sum / n;
}
while (n_batch--) {
for (int i = 1; i <= n; i++) { //cal X_hat
for (int j = 1; j <= c; j++) {
X_hat[i][j] = (X[i][j] - mu[j]) / sqrt(var[j] + eps);
}
}
for (int i = 1; i <= c; i++) {
moving_mean[i] = moving_mean[i] * momentum + (1.0 - momentum) * mu[i];
}
for (int i = 1; i <= c; i++) {
moving_var[i] = moving_var[i] * momentum + (1.0 - momentum) * var[i];
}
for (int i = 1; i <= n; i++) { //cal out
for (int j = 1; j <= c; j++) {
out[i][j] = X_hat[i][j] * Gamma[j] + Beta[j];
}
}
}
//last
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= c; j++) {
X_hat[i][j] = (X[i][j] - moving_mean[j]) / sqrt(moving_var[j] + eps);
}
}
for (int i = 1; i <= n; i++) { //cal out
for (int j = 1; j <= c; j++) {
out[i][j] = X_hat[i][j] * Gamma[j] + Beta[j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= c; j++) {
cout << out[i][j] << " ";
}
cout << endl;
}
return 0;
}
E Antinomy与黑风海
大意:
有向图,从点1出发,每次去其他的点做任务,做完回到1点后才能去下一个点做任务,问做完全部任务需要走多远
思路:
很容易看出来前去做任务就是求点1到其他所有点的距离即可,那么怎么求回来的距离呢?直接把每条边反向,重新建一遍图即可
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PII;
int const MAXN = 1e6 + 10, MAXM = 2 * MAXN;
int n, m, T, h[MAXN], e[MAXM], ne[MAXM], idx, w[MAXM], st[MAXN];
LL dis[MAXN];
struct Edge {
int u, v, c;
}edge[MAXM];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dijkstra() {
memset(dis, 0x3f, sizeof dis); // 初始化距离为无穷
priority_queue<PII, vector<PII>, greater<PII> > q; // 定义一个按照距离从小到大排序的优先队列,第一维:距离,第二维:点
dis[1] = 0; // 一开始源点距离为0
q.push({0, 1}); // 把源点信息放入队列
while (q.size()) { // 每个点只出入队列一次
auto t = q.top();
q.pop();
LL distance = t.first;
int ver = t.second; // 最小距离和相对应的点
if (st[ver]) continue; // 这个操作保证每个点只出入队一次,因为队列里面可能会出现{dis1[3], 3}, {dis2[3], 3}的情况,这样保证dis1[3]<dis2[3]时,3号点只进出入队一次
st[ver] = 1; // 标记,因为dijkstra的贪心策略保证每个点只需要进出队一次
for (int i = h[ver]; ~i; i = ne[i]) { // 遍历ver的邻接点
int j = e[i];
if (dis[j] > distance + (LL)w[i]) {
dis[j] = distance + (LL)w[i];
q.push({dis[j], j}); // 这里不需要判断st,因为一旦更新发现更小必须放入队列
}
}
}
}
int main() {
memset(h, -1, sizeof h);
idx = 0;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
edge[i] = {a, b, c};
add(a , b, c);
}
dijkstra();
LL res = 0;
for (int i = 1; i <= n; ++i) {
h[i] = -1;
st[i] = 0;
if (i != 1) res += dis[i];
}
idx = 0;
for (int i = 1; i <= m; ++i) {
int a = edge[i].u, b = edge[i].v, c = edge[i].c;
add(b, a, c);
}
dijkstra();
for (int i = 2; i <= n; ++i) {
res += dis[i];
}
cout << res;
return 0;
}
F Antinomy与紫水宫
大意:
给出一个树,树上每对距离小于3的节点颜色都不能相同,现在有m种颜色,问有多少种涂色方法
思路:
树形dp,首先固定每个节点与其父节点的颜色,那么子节点的颜色数利用组合数可以算出来(A_{m=2}^{子节点数量}),然后向父节点传递即可。注意根节点处因为没有父节点所以要单独写
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, mod = 1e9 + 7;
typedef long long LL;
int n, m;
vector<int> mp[N];
int fact[N], infact[N];
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
// 预处理
void init() {
fact[0] = infact[0] = 1;
for (int i = 1; i < N; ++i) {
fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
}
int Cnm(int a, int b) {
return (LL)fact[a] * infact[b] % mod * infact[a - b] % mod;
}
int res = 1;
void dfs(int now, int fa) {
res = (LL)res * Cnm(m - 2, mp[now].size() - 1) % mod *
fact[mp[now].size() - 1] % mod; //求Anm;
for (int i = 0; i < mp[now].size(); i++) {
int ne = mp[now][i];
if (ne == fa) continue;
dfs(ne, now);
}
}
int main() {
cin >> n >> m;
init();
for (int i = 0; i < n - 1; i++) {
int x, y;
cin >> x >> y;
mp[x].push_back(y);
mp[y].push_back(x);
}
res = (LL)res * Cnm(m, mp[1].size() + 1) % mod * fact[mp[1].size() + 1] % mod;
for (int i = 0; i < mp[1].size(); i++) {
dfs(mp[1][i], 1);
}
cout << res << endl;
return 0;
}
G Antinomy与伊修加德
大意:
Antinomy想买一些盘子来装拉拉肥,这样可以打包卖出去,每个拉拉肥不能拆开卖。
每个拉拉肥都有售价,第i个拉拉肥卖出去后的售价是(p_i)金币。
每个盘子也需要花钱买,第iii个盘子需要花(c_i)金币,能装(b_i)个拉拉肥。
每个拉拉肥可以卖也可以不卖,但卖就必须要放在一个盘子里。Antinomy的利润就是卖出去的拉拉肥售价总和,减去盘子的花费。
思路:
类似01背包的问题,每个盘子取或不取,价值就是拉拉肥的收益-盘子价格,体积就是拉拉肥的个数,注意要先将拉拉肥按照价值排序,因为拉拉肥的体积相同,那么肯定是优先卖价值高的
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, m, p[N], b[N], c[N], pre[N],dp[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
sort(p + 1, p + 1 + n, greater<int>());
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + p[i];
}
for (int i = 0; i < m; i++) {
cin >> b[i] >> c[i];
}
int res = 0;
for (int i = 0; i < m;i++){
for (int j = n; j >= 0;j--){
if(j>=b[i])
dp[j] = max(dp[j], dp[j - b[i]] + pre[j] - pre[j - b[i]] - c[i]);
else
dp[j] = max(dp[j], pre[j] - c[i]);
res = max(res, dp[j]);
}
}
cout << res << endl;
return 0;
}
H Antinomy与太阳神草原
大意:
思路: