校赛
B 文氏图面积
算两个圆覆盖的总面积。
面积即为中间几何体 + 两边扇形面积
如下
扇形面积即为:
其中根号部分可由海伦公式得到
三角形面积(几何体由两个三角形组合而成)
因此,
设两圆圆心距为\(d\),半径分别为 \(r_1\) 和 \(r_2\)(不妨设 \(r_1 \ge r_2\)),且两圆相交(\((|r_1 - r_2| < d < r_1 + r_2\))。则相交部分面积为:
最终答案即为
#include <bits/stdc++.h>
using namespace std;
typedef pair<double , double>pii;
struct c//circle
{
double r;//半径
pii pt;//point
};
double pi = acos(-1);
void input(c &qwq)
{
cin >> qwq.pt.first >> qwq.pt.second >> qwq.r;
}
void solve()
{
c c1, c2;
int n;cin >> n;
if(n == 1)
{
input(c1);
cout << fixed << setprecision(4) << c1.r * c1.r * pi << '\n';
}
else
{
input(c1),input(c2);
double dis = sqrt((c1.pt.first - c2.pt.first) * (c1.pt.first - c2.pt.first) + (c1.pt.second - c2.pt.second)*(c1.pt.second - c2.pt.second));
if(c1.r < c2.r) swap(c1,c2);
if(dis >= c1.r + c2.r) cout << fixed << setprecision(4) << c1.r * c1.r * pi + c2.r * c2.r * pi << '\n';
else if(dis + c2.r <= c1.r) cout << fixed << setprecision(4) << c1.r * c1.r * pi << '\n';
else
{
//如果是钝角的情况呢,那么在这种情况下我们arcos函数是不是就不适用了?
//不会出现因为,因为我们计算角度的时候直接用的是dis/r这种算法所以算出来默认是锐角的那一边(注意显然这两个角,要么同时锐角,要么同时钝角)
double r1 = c1.r, r2 = c2.r;
double d = dis;
double angle1 = acos((d*d + r1*r1 - r2*r2) / (2*d*r1));//dis1/r1
double angle2 = acos((d*d + r2*r2 - r1*r1) / (2* d*r2));//dis2/r2
//现在已经知道三条边求面积,直接套一下海伦公式
//p = (a + b + c) /2,s = sqrt(p * (p - a) *(p - b) *(p - c));
double res = (pi - angle1) * r1*r1 + (pi - angle1) *r2*r2 + sqrt((-d+r1+r2)*(d+r1-r2)*(d-r1+r2)*(d+r1+r2))/2;//扇形+ 中间几何形状
cout << fixed << setprecision(4) << res << '\n';
}
}
}
int main()
{
int t;cin >> t;
while(t -- )solve();
return 0;
}
C 因子代数0(easy version)
因子代数是指基于全体因子的整数函数。考虑如下因子代数:
记 \(S(n)\) 为正整数 \(n\) 的所有因子按照从小到大的顺序正负交替的平方代数和,
如\(S(6)=1^2−2^2+3^2−6^2=−30\)
输入格式
整数 \(n( 1 \le n \le 1000 )\)。
输出格式
输出 \(( S(1) + S(2) + \cdots + S(n) )\) 的值。
思路:暴力算因子然后求和就好了呗
#include <bits/stdc++.h>
#define ll long long
using namespace std;
void solve()
{
int n;cin >> n;
ll res = 0;
for(int i = 1; i <= n ;i ++)
{
ll t = 0;
for(int j = 1 ;j <= n ;j ++)
{
if(i % j == 0)
{
if(t <= 0) t += j * j;//(这个是纯感觉出来的QWQ,没证明不过对了,当然也可以用cnt来判断正负)
else t -= j * j;
}
}
res += t;
}
cout << res << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
solve();
return 0;
}
D 因子代数(hard version)
和上一题的区别只有n的范围变成了\((1≤n≤2×10^6)\)导致\(n^2\)的算法失效了,得优化
思路:第一反应可能会想到优化因子遍历但是这种方法只能优化到\(O(n\sqrt{n})\),数据范围为2e6,计算次数会到2e9仍然是超时的所以还不够
然后想到可以用筛法来求因子,因为这道题n的大小是2e6筛法遍历是完全足够的,说到筛法就想到了d3的Bob and Alice的game那道题传送门QWQ
这里采用埃氏筛去筛出所有因子(欧拉筛只筛最小质因子,不会筛全,所以这里挑选埃氏筛QWQ)
以下是代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e6 + 10;
ll e[N];
bool np[N];
void elurs()
{
int n = N - 5;
for(int i = 1 ;i <= n ;i ++) e[i] = 1;
for(int i = 2 ;i <= n;i ++)
{
if(!np[i])
{
e[i] = 1 - (ll)i * i;
}
else e[i] += (e[i] <= 0 ? (ll)i * i : -(ll)i*i);
for(int j = i * 2; j <= n ; j += i)
{
np[j] = true;
if(e[j] <= 0) e[j] += (ll)i * i;
else e[j] -= (ll)i * i;
}
}
}
void solve()
{
int n;cin >> n;
ll res = 0;
for(int i = 1 ;i <= n ;i ++) res += e[i];
cout << res << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
elurs();
solve();
return 0;
}
//Divisive BattleB的代码附上来一同参考
#include <bits/stdc++.h>
using namespace std;
const int M = 1e6 + 10, N = 2e5 + 10;
int p[M], cnt, minfact[M];
bool np[M];
inline int read()
{
int x = 0, f = 1, c;while(!isdigit(c = getchar())) if(c == '-') f = -1;
while(x = x * 10 + (c ^ 48), isdigit(c = getchar()));
return x *f;
}
void elurs()
{
for(int i = 2 ;i <= M ;i ++)
{
if(!np[i])
{
p[++ cnt] = i;
minfact[i] = cnt;
}
for(int j = 1; p[j] <= M / i ; j ++)
{
np[p[j] * i] = true;
minfact[p[j] * i] = j;
if(i % p[j] == 0)
{
minfact[i] = j;
break;
}
}
}
}
void solve()
{
int n = read();
vector<int>a(n);
for(auto &i : a) i = read();
vector<int>b = a;
sort(b.begin(), b.end());
if(a == b)
{
cout << "Bob\n";
return;
}
auto isok = [&](int x) -> bool
{
if(x == 1) return false;
int k = x;
while(k % p[minfact[x]] == 0) k /= p[minfact[x]];
for(int i = p[minfact[x]] + 1 ;i <= x / i; i ++)
{
if(k % i == 0) return true;
}
if(k > 1) return true;
return false;
};
vector<int>c, d;
for(int i = 0 ;i < n ;i ++)
{
if(a[i] == 1)
{
c.push_back(1);
}
else c.push_back(p[minfact[a[i]]]);
if(a[i] == p[minfact[a[i]]] ) continue;
if(isok(a[i]))
{
cout << "Alice\n";
return;
}
}
d = c;
sort(d.begin(), d.end());
if(d == c)
{
cout << "Bob\n";
}
else cout << "Alice\n";
}
int main()
{
elurs();
int t = read();
while(t --) solve();
return 0;
}
----- 分界线QWQ----
//优化因子取法,太久没写了也写一些hhh
#include <bits/stdc++.h>
#define ll long long
using namespace std;
void solve()
{
int n;cin >> n;
vector<int>res;
for(int i = 1 ;i <= n/i ; i ++)
{
if(n % i == 0)
{
res.push_back(i);
if(i != n/i) res.push_back(n / i);
}
}
for(int i = 0 ;i < res.size() ;i ++) cout << res[i] << ' ';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
solve();
return 0;
}
E 智慧增长之路
题目描述
在一个充满智慧和奇迹的小镇上,住着一个聪明而好奇的小W。小W喜欢面临挑战,通过巧妙的思考和勤奋努力来解决问题。一天,他发现了一个神奇的数字谜题,这个谜题隐藏在一个由n个整数组成的数组a中。
这个数字谜题的规则是,小W可以选择任意相邻的两个数字,只要前一个数字小于等于后一个数字,就可以将前一个数字增加1。而小W最多可以执行k次这样的操作。他的目标是通过这些操作,使得整个数组中的最大值尽可能地大。
小W迫不及待地开始了解这个数字谜题的奥秘。他意识到这是一个关于智慧增长的旅程,每一步都是他通向成功的一个台阶。小W决定运用他的数学智慧,巧妙地设计一系列的操作,使得数组中的每个元素都能够尽量增加。请你帮小W计算出在最优操作下最多执行k次操作后数组中的最大值。
输入格式
第一行包括一个整数T(1≤T≤100)(表示有T组测试样例)。
对于每种测试样例,第一行包括两个整数n (\(2≤n≤1000\))和k (\(1≤k≤10^8\)),分别为数组a的长度和可以执行的最大操作数。
每个测试用例的第二行包含n个整数\(a_1,\cdots,a_n\) (\(1≤ai≤10^5\))——数组a的元素。保证所有测试用例的n之和不超过1000。
输出格式
输出T行,对于每个测试用例,输出一个整数——最优操作下执行最多k次操作后数组的最大可能值。
思路1(贪心模拟):题目给出n是1000,所以大概是要求在\(O(n^2)\)的时间复杂度内完成。
但这里还是进行了一定程度的剪枝:
有些情况根本不用考虑,比如从右往左看(因为这里的操作要求后面大前面小从后往前操作)的时候如果\(a_i\)小于等于\(a_{i - 1}\)那么我们根本不用考虑\(a_i\),因为对于\(a_{i}\)前面的最终调出来的最大值,我们\(a_{i - 1}\)也一定能调出且最终得到的最大值比其大。(这是我们优化的重点,有种单调队列的味道)
而对于前面变大了的情况我们就要去遍历一遍看看了。(注意构造出来的数的形状就是一个阶梯,性价比最高)在遍历的过程中我们记录一下操作次数和当前最大值。
但是注意这里有一个阶梯下沉操,因为此时用完所有操作次数后得到的不一定是我们所要求的最大值,可能后面还有阶梯可以走(就是省下一些操作次数,可能后面的阶梯可以走的更远,答案可能在更远的地方),不过最起始点高的阶梯就是当前这个从tail的下一个开始增长的这个。
这个阶梯式跳跃检查的思路真的很惊艳,收了QWQ。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
void solve() {
int n, k; cin >> n >> k;
vector<int> a(n + 1, 0);
for(int i = 1; i <= n; ++ i) cin >> a[i];
ll tail = n,maxv = a[tail], nowb = 0, price = 0;
bool flag = true;//记录第一次进入while循环
while(flag || price > k) {
flag = false;
int b = nowb; price = 0;//b为增量,price为当前的代价
for(int i = tail - 1; i > 0; -- i) {
b ++;
price += a[tail] + b - a[i];
if(price <= k) maxv = max((ll)a[tail] + b, maxv);
if(a[i] >= a[tail] + b) tail = i, b = nowb = 0, price = 0;//找到新tail,从最新的tail开始更新,并初始化新状态
}
-- nowb;//状态递减,模拟整个阶梯下降一层
}
cout << maxv << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while(t --) solve();
return 0;
}
G 小a的卡牌游戏
交互题
题目如下
小 a 最近迷上了卡牌。某天,他在教室里遇到了小 c。
小 c 给小 a 出了一道题:他切出了三堆卡牌,每堆都有n张牌,每张牌有一个点数p(\(1≤p≤1000\)),在一开始,小 c 会告诉你三堆牌顶点数之和。
小 a 不知道任何具体牌面,但是他可以选择一堆并移走其当前最上面一张牌,小 c 会立刻告诉你当前三堆牌顶点数之和。
小 a 的目标是在最多3n次操作下,求出三堆牌顶点数之和可能达到的最大值。
#include <bits/stdc++.h>
using namespace std;
void ask(int type, int &pre, int &maxv)
{
cout << type<<'\n';
cout.flush();
int x;cin >> x;
pre = x;
maxv = max(maxv , x);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, s;cin >> n >> s;
int pre = s, maxv= s,t = s;
for(int i = 1 ;i < n ;i ++) ask(1, pre, maxv);
s += maxv - t;
maxv = pre;
t = pre;
for(int i = 1 ;i < n ;i ++) ask(2, pre, maxv);
s += maxv - t;
maxv = pre;
t = pre;
for(int i = 1 ;i < n ;i ++) ask(3, pre, maxv);
s += maxv - t;
maxv = pre;
cout << "! "<< s << '\n';
cout.flush();
return 0;
}
I 没有87
输出a~b之间的所有没有87的数字(a,b范围为小于1e18)
思路:常规数位dp,直接搜索即可以下是代码,\(时间复杂度 = 状态数 * 状态计算\),也就是说我们最多只有8800次计算,所以数位dp一般是很快的QWQ
以下是代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll pow10[20];
ll dp[20][11][2][2];
bool vis[20][11][2][2];//代表当前情况已经访问 ,也可以用时间戳优化,就是再多加一维即vis[20][11][2][2][]
ll dfs(vector<int>&a, int pos, int pre, bool lim, bool have87)
{
if(a.size() == pos) return have87;//如果已经走完了,看一下合法情况,如果合法就直接返回
if(vis[pos][pre][lim][have87]) return dp[pos][pre][lim][have87];
ll res = 0;
if(!lim && have87)//一步剪枝,如果当前是非限制状态并且以前前面出现过87了就直接排列组合返回即可
{//当然你不写也可以。也是可以AC的
vis[pos][pre][lim][have87] = true;
return dp[pos][pre][lim][have87] = pow10[a.size() - pos];
}
int lea = (lim ? a[pos] : 9);//看一下当前位遍历是否存在限制
for(int i = 0; i <= lea ; i ++)
{
int npre = (pre == 10 && !i ? 10 : i);//看一下下一层的pre是什么,注意这里要多考虑一个前导零状态,用来区分如907和7这种(7在我们眼里是007)
bool nlim = lim && (i == lea), nhave87 = have87 || (pre == 8 && i == 7);
res += dfs(a, pos + 1 , npre, nlim, nhave87);
}
vis[pos][pre][lim][have87] = true;
return dp[pos][pre][lim][have87] = res;//记忆化
}
ll cal(ll n)
{
//这里没给t的范围用不了时间戳所以只能这样每步初始化了
memset(dp, 0, sizeof dp);
memset(vis, 0, sizeof vis);
ll k = n;
vector<int>a;
while(k)
{
a.push_back(k % 10);
k /= 10;
}
//分解以后是最低位在
reverse(a.begin(),a.end());
return n - dfs(a, 0, 10, true, false);
}
void solve()
{
ll a, b;cin >> a >> b;
cout << cal(b) - cal(a - 1) << '\n';//类前缀和
}
int main()
{
pow10[0] = 1;
for(int i = 1 ;i < 20 ;i ++) pow10[i] = pow10[i - 1] * 10;
int t;cin >> t;
while(t -- ) solve();
return 0;
}
J Kirito的最大异或值
思路:01字典树求最大异或对,至于这里要求的找出其中异或的的数要求小于m,我们可以通过离线询问的技巧进行处理(就是存储询问,然后按照m升序排个序,这样子我们就可以根据小于等于m这个条件去构造我们的字典树,之后要做的就是求一下当前字典树的最大异或值罢了)
代码如下
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10, M = N * 31;
class Trie
{
private:
vector<array<int, 2>>son;
int idx;
public:
Trie(int n)
{
son.resize(n + 1);
idx = 0;
}
void insert(int x)
{
int pt = 0;
for(int i = 30 ;i >= 0 ;i -- )//int 2^31~0
{
int t = (x >> i) & 1;
if(!son[pt][t]) son[pt][t] = ++ idx;
pt = son[pt][t];
}
}
ll query(int x)
{
ll res = 0, pt = 0;//注意这里直接返回的就是异或值
for(int i = 30 ;i >= 0 ;i -- )
{
int t = (x >> i) & 1;
if(son[pt][t^1]) pt = son[pt][t^1], res |= (1LL << i);
else if(son[pt][t]) pt = son[pt][t];
else return -1;
}
return res;
}
};
struct qr
{
int x, m, id;
};
void solve()
{
int n;cin >> n;
vector<int>a(n);
Trie tree((n + 1) * 31);
for(int i = 0 ;i < n ;i ++ ) cin >> a[i];
int q;cin >> q;
vector<qr>Query(q);
for(int i = 0; i < q ;i ++)
{
qr qwq;
cin >> qwq.x >> qwq.m;
qwq.id = i;
Query[i] = qwq;
}
sort(a.begin(), a.end());
sort(Query.begin(), Query.end(), [&](qr i, qr j)
{
return i.m < j.m;
});
vector<ll>res(q);
int pt = 0;
for(int i = 0 ;i < q ;i ++)
{
while(pt < n && a[pt] <= Query[i].m) tree.insert(a[pt ++ ]);
res[Query[i].id] = tree.query(Query[i].x);
}
for(int i = 0 ;i < q; i ++) cout << res[i] << '\n';
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
solve();
}

浙公网安备 33010602011771号