题目 | A | B | C | D | E | F | G | H | I | J | K | L | M |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
solved | ✔ | - | - | - | - | - | ✔ | ✔ | - |
✔:比赛时通过;:赛后通过;⚪:比赛时尝试了未通过;-:比赛时未尝试
solved by Sstee1XD & Tryna. 0:36(+3)
题意 n n n个石子,每个石子从 1 1 1到 n n n编号,每次可以取 1 1 1- k k k个连续的石子,问谁先取完。
题解:我们特判 n n n为 0 0 0和 k k k为 1 1 1的情况,对于其余情况,先手都可以做出相应的选择达到必胜态。
#include
using namespace std;
int n, k;
int main() {
scanf("%d %d", &n, &k);
if(n == 0) puts("Austin");
else if (k == 1 && n % 2 == 0) puts("Austin");
else puts("Adrien");
return 0;
}
solved by Tryna. (-)
题意: 求覆盖了所有点的最小球半径
题解: 裸题,跑一边模拟退火,代码中更新pos值的时候理解了很久。总的来说就是当前温度越高,向最远点移动的幅度越大,当前温度越低,向最远点移动的幅度越小
#include
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-8
const int maxn = 110;
const double startT = 100000;
struct Point{
double x, y, z;
}P[maxn];
double Dist(Point a, Point b){
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z));
}
int n;
double SA(){
double delta = 0.98, T = startT, ans = inf;
Point pos = {0, 0, 0};
while(T > eps){
Point maxP = P[1];
for(int i = 2; i <= n; i++){
if(Dist(pos, P[i]) > Dist(pos, maxP))
maxP = P[i];
}
ans = min(ans, Dist(pos, maxP));
pos.x += (maxP.x - pos.x) * T / startT;
pos.y += (maxP.y - pos.y) * T / startT;
pos.z += (maxP.z - pos.z) * T / startT;
T *= delta;
}
return ans;
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lf %lf %lf", &P[i].x, &P[i].y, &P[i].z);
printf("%.15f\n", SA());
return 0;
}
solved by Tryna. (-)
题意: 计算等边三角形的个数
题解: 打表找规律,打表的思路就是把三角形放入坐标系中,O(n^3)暴力枚举每个顶点,如果三条边长度相同就计数。
打完表发现前几个数是这样的: 0 1 5 15 35 70 126 210
下面的方法将会用到这里的知识:差分的应用
所以我们对上面那个数列不断差分。
0 1 5 15 35 70 126 210
1 4 10 20 35 56 84
3 6 10 15 21 28
3 4 5 6 7
1 1 1 1
0 0 0
所以前n项和 S ( n ) = 1 C n + 1 2 + 3 C n + 1 3 + 3 C n + 1 4 + 1 C n + 1 5 S(n) = 1C_{n+1} ^ 2 + 3C_{n+1} ^ 3 + 3C_{n+1} ^ 4 + 1C_{n+1} ^ 5 S(n)=1Cn+12+3Cn+13+3Cn+14+1Cn+15
化简得: S ( n ) = n ∗ ( n + 1 ) ∗ ( n + 2 ) ∗ ( n + 3 ) ∗ ( n + 4 ) 120 S(n) = \frac{n*(n+1)*(n+2)*(n+3)*(n+4)}{120} S(n)=120n∗(n+1)∗(n+2)∗(n+3)∗(n+4)
所以第n项 f ( n ) = S ( n ) − S ( n − 1 ) f(n) = S(n) - S(n-1) f(n)=S(n)−S(n−1)
化简得: f ( n ) = n ∗ ( n + 1 ) ∗ ( n + 2 ) ∗ ( n + 3 ) 24 f(n) = \frac{n*(n+1)*(n+2)*(n+3)}{24} f(n)=24n∗(n+1)∗(n+2)∗(n+3)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define eps 1e-10
#define endl "\n"
const int maxn = 1000010;
const int mod = 1e9 + 7;
const double pi = acos(-1.0);
int moven2[10][5] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
int moven1[10][5] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int quick_power(ll n, ll k){
int ans=1;
while(k){
if(k&1){
ans=(ans*n)%mod;
}
n=n*n%mod;
k>>=1;
}
return ans%mod;
}
int t;
ll n;
int main() {
// ios::sync_with_stdio(false);
// cin.tie(0);cout.tie(0);
scanf("%d", &t);
while(t--){
scanf("%lld", &n);
ll ans = quick_power((ll)24, mod - 2);
ans = ans * n % mod * (n + 1) % mod * (n + 2) % mod * (n + 3) % mod;
printf("%lld\n", ans);
}
return 0;
}
solved by lllllan. 01:26(+)
题意: n个英雄和m个怪兽,每个英雄都有对应的可以击败的怪兽并且只能选择其中一只。现有k瓶药水,每个英雄最多喝一瓶然后可以多击败一只怪兽。问最多可以击败多少只怪兽。
题解: 相比最经典的匹配问题,一个男朋友对应一个女朋友,现在可以一夫多妻了【不是】。又不像多重匹配那么麻烦,每个英雄最多也只能击败两只怪兽而已。所以就是简单跑两次匹配就可以了。 然后注意有K瓶药水的限制。
#include
using namespace std;
#define _for(i, a, b) for(int i = (a); i <= (b); ++i)
const int N = 510;
int sum;
int n, m, k, t, x;
int vis[N], match[N];
vector<int> G[N];
bool dfs (int u) {
for(auto v : G[u]) {
if(match[v] == u) continue;
if(!vis[v]) {
vis[v] = 1;
if(!match[v] || dfs(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
void solve () {
memset(match, 0, sizeof match);
_for(i, 1, n) {
memset(vis, 0, sizeof vis);
if(dfs(i)) sum++;
}
for(int i = 1; i <= n && k; i++) {
memset(vis, 0, sizeof vis);
if(dfs(i)) sum++, k--;
}
}
int main() {
scanf("%d%d%d", &n, &m, &k);
_for(i, 1, n) {
scanf("%d", &t);
while(t--) {
scanf("%d", &x);
G[i].push_back(x);
}
}
solve();
printf("%d\n", sum);
return 0;
}
solved by Sstee1XD. 1:11(+)
题意:给定序列a,定义
让你计算 ∑ i = 1 n ∑ j = i n f a c ( i , j ) \sum_{i = 1}^n\sum_{j = i}^nfac(i, j) ∑i=1n∑j=infac(i,j)。
题解:我们设 p r e v pre_v prev 为 v v v最后出现的位置。对于 a i a_i ai的质因子 v v v,它对于答案的贡献为对前面的贡献加上对后面的贡献,即 ( n − i + 1 ) ∗ ( i − p r e v − 1 ) + n − i + 1 (n - i +1)*(i-pre_v-1)+n-i+1 (n−i+1)∗(i−prev−1)+n−i+1 。
#include
using namespace std;
#define endl "\n"
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err() { cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
cout << arg << ' '; err(args...);}
inline int rd() {
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e6 + 7, N = 1e3;
int prime[N + 10], cnt, flag[N + 10];
void isprime() {
for (int i = 2; i <= N; ++i) {
if (!flag[i]) {
prime[++cnt] = i;
}
for (int j = 1; j <= cnt; ++j) {
if (1ll * prime[j] * i > N) break;
flag[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
}
}
}
int n, pre[maxn], a[maxn];
vector<int>G[maxn];
void run() {
for (int i = 1; i <= n; ++i) {
a[i] = rd();
if (G[a[i]].size() == 0 && a[i] != 1) {
int tmp = a[i];
for (int j = 1; j <= cnt; ++j) {
if (tmp % prime[j] == 0) {
G[a[i]].push_back(prime[j]);
while (tmp % prime[j] == 0) {
tmp /= prime[j];
}
if (tmp == 1) break;
}
}
if (tmp > 1) G[a[i]].push_back(tmp);
}
}
ll ans = 0;
for (int i = 1; i <= n; ++i) {
for (auto v : G[a[i]]) {
ans += (ll)(n - i + 1) * (ll)(i - pre[v] - 1) + (ll)(n - i + 1);
pre[v] = i;
}
}
printf("%lld\n", ans);
}
int main() {
isprime();
n = rd();
run();
return 0;
}
solved by Sstee1XD. (-)
题意:每次可以选择上下左右四个方向,一起移动所有的 1 1 1,如果碰到 0 0 0或者墙就不动,最后要在 50000 50000 50000步内使所有 1 1 1聚在一起。题目保证1不会转圈圈。
题解:因为保证的存在,所以就直接随机输出 50000 50000 50000个方向就好了(为什么会这么玄学?)。
#include
using namespace std;
int main() {
string s;
int n, m;
cin >> n >> m;
while (n--) cin >> s;
for (int i = 1; i <= 50000; ++i) {
int x = rand()%4;
if (x == 0) putchar('U');
if (x == 1) putchar('D');
if (x == 2) putchar('R');
if (x == 3) putchar('L');
}
return 0;
}
solved by Sstee1XD. (-)
题意: 给你两个字符串 s s s和 t t t,对于 t t t中所有的前缀串 k k k,你要在 s s s中找到所有的子串 s i j s_{ij} sij,使得 s i j s_{ij} sij长度大于 k k k,并且将 s i j s_{ij} sij与 k k k拼接后得到的字符串为回文串。问你最终的数量和。
题解:为了让最后的字符串是回文串,对于前缀串 k k k,我们要先在 s s s中找到与前缀串 k k k一样的子串,再向后扩展,使得扩展的那部分字符串为回文串,即可实现。
我们先将 t t t进行反转,然后去遍历 s s s,那么现在的问题就转换成了找两个字符串的最长后缀(因为对于这部分所有的子串,他们的答案是一样的,所以最后求出来乘以最长后缀的长度就行了)。这里我们用哈希+二分
来实现。
找出最长后缀后,我们假设当前遍历到 s i s_i si,那么接下来我们要求以 s i + 1 s_{i+1} si+1为起点的回文串数量。想到回文串就想到了马拉车
。马拉车可以求以每个点为中心的最长回文半径,那么对于以这个点为终点,以 i i i - 最长回文半径
的位置为起点,终点对于这段区间的贡献值为1。所以就转化成了区间修改,最终求每个点的权值的差分数组
问题。稍微推一下就能从马拉车字符串推回到原字符串数组。
#include
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxn=1e6 + 7;
const ull base = 131;
char s[maxn], t[maxn];
char ma[maxn<<1];
ll mp[maxn<<1], paw[maxn];
ull gap[maxn], hashS[maxn], hashT[maxn];
void init() {
gap[0] = 1;
for (int i = 1; i <= 1000000; ++i) {
gap[i] = gap[i - 1] * base;
}
}
void makeHash(char *s, ull *hash) {
int len = strlen(s);
for (int i = 0; i < len; ++i) {
hash[i + 1] = hash[i] * base + (ull)s[i];
}
}
ull getHash(int l, int r, ull *hash) {
return hash[r + 1] - hash[l] * gap[r - l + 1];
}
void manacher(char *s){
int len = strlen(s);
int l=0;
ma[l++]='$';
ma[l++]='#';
for(int i = 0;i < len; ++i){
ma[l++] = s[i];
ma[l++] = '#';
}
ma[l] = 0;
ll mx = 0, id = 0;
for (int i = 0; i < l; ++i){
mp[i] = mx > i? min(mp[2 * id - i], mx - i) : 1;
while (ma[i + mp[i]] == ma[i - mp[i]]) mp[i]++;
if(i + mp[i] > mx){
mx = i + mp[i];
id = i;
}
}
for (int i = 1; i < l; i++) {
paw[(i >> 1) - (mp[i] >> 1)]++;
paw[(i >> 1)]--;
}
for (int i = 1; i < len; ++i) {
paw[i] += paw[i - 1];
}
}
void solve() {
int len = strlen(s);
int lent = strlen(t);
int l, r, mid;
ll ans = 0;
ll maxx;
for (int i = 0; i < len - 1; ++i) {
l = 1, r = i + 1;
maxx = 0;
while (l <= r) {
mid = l + r >> 1;
if (getHash(i - mid + 1, i, hashS) == getHash(lent - mid, lent - 1, hashT)) {
maxx = mid;
l = mid + 1;
}
else r = mid - 1;
}
ans += maxx * paw[i + 1];
}
printf("%lld\n", ans);
}
int main(){
init();
scanf("%s %s", s, t);
reverse(t, t + strlen(t));
manacher(s);
makeHash(s, hashS);
makeHash(t, hashT);
solve();
return 0;
}