#include
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int mod = 998244353;
const ull base = 2333;
const int N = 1e6 + 10;
const int M = 1e5 + 10;
int n, m;
map<ull, int> mp;
//先将字符串的所有的后缀哈希存储 统计出现的次数
void getHash(string s) {
ull x = 0, p = 1;
int sz = s.length();
for (int i = sz - 1; i >= 0; i--) {
x += s[i] * p;
p *= base;
mp[x]++;
}
}
int nxt[N];
// kmp的next数组 下标从0开始
void getNext(string s) {
int sz = s.length();
int k = -1;//当前前后缀能匹配出来的最大的长度 -1
nxt[0] = k;
for (int i = 1; i < sz; i++) {
while (k > -1 && s[k + 1] != s[i]) k = nxt[k];
if (s[k + 1] == s[i]) k++;//如果还能匹配上
nxt[i] = k;
}
}
string s[M];
int cnt[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> s[i];
getHash(s[i]);
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
int sz = s[i].length();
ull x = 0;
for (int j = 0; j < sz; j++) {
x = x * base + s[i][j];
cnt[j] = mp[x];//统计 前缀长度为j时 能与其相匹配的后缀的个数
}
getNext(s[i]);
for (int j = 0; j < sz; j++) {
if (nxt[j] > -1) {
cnt[nxt[j]] -= cnt[j];
}
/*
* 将重复统计的减掉
* as: 当 aba 与 aba 自身进行统计的时候
* cnt[a]与 cnt[aba]都会被下面的for统计进去
* 更优解当然是保留最大长度的aba 所以 需要从cnt[a]里 减去cnt[aba]的部分
* 而通过nxt[j]可以很快的找到 在j前面
* 存在与前缀相匹配的后缀 且前缀部分从0到下标为nxt[j] 的 前缀部分最后那个位置
*
* 前面提到 cnt[i]表示前缀部分为0~i位置 存在与之相匹配的后缀的个数
* 所以写法是 cnt[nxt[j]]-=cnt[j]
* */
}
for (int j = 0; j < sz; j++) {
ans = (ans + ((1ll * cnt[j] * (j + 1) % mod) * (j + 1) % mod)) % mod;
// 如果改成 ans+=... ans%=mod 这样会T
}
}
cout << ans << endl;
return 0;
}
#include
using namespace std;
const double eps = 1e-8;
const int N = 1e6 + 10;
int n;
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
//判断小数和0是否等于
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) { x = _x, y = _y; }
void input() { scanf("%lf%lf", &x, &y); }//输入
void output() { printf("(%.2f %.2f)\n", x, y); } //输出
Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }
Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }
Point operator*(const double &k) const { return Point(x * k, y * k); }
Point operator/(const double &k) const { return Point(x / k, y / k); }
bool operator==(Point b) const { return sgn(x - b.x) == 0 && sgn(y - b.y) == 0; }
bool operator<(Point b) const { return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x; }
double operator^(const Point &b) const { return x * b.y - y * b.x; }
//叉积
double operator*(const Point &b) const { return x * b.x + y * b.y; }
//点积
double distance(Point p) { return hypot(x - p.x, y - p.y); }
//两点之间的距离
Point rotLeft() { return Point(-y, x); }
//逆时针旋转90°
};
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
bool operator==(Line v) { return (s == v.s) && (e == v.e); }
Point cross_point(Line v) {
double a1 = (v.e - v.s) ^(s - v.s);
double a2 = (v.e - v.s) ^(e - v.s);
return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
}//求两直线的交点 前提:要保证直线不平行或重合
};
struct Circle {
Point p;//圆心
double r;//半径
Circle() {}
Circle(Point a, Point b, Point c) {
Line u = Line((a + b) / 2, ((a + b) / 2) + ((b - a).rotLeft()));
Line v = Line((b + c) / 2, ((b + c) / 2) + ((c - b).rotLeft()));
p = u.cross_point(v);
r = p.distance(a);
}//三角形的外接圆
};
Point p[N];
map<Point, int> mp;
Circle cir;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
p[i].input();
}
if (n <= 2) {
printf("%d\n", n);
return 0;
}
int res = 1;//肯定有一个点 在 与原点共圆
//枚举两点+原点 所组成的三角形 产生外接圆
for (int i = 1; i <= n; i++) {
mp.clear();
// 存在 p[i] p[j]形成的外接圆 与 p[j] p[k]所形成的外接圆是同一个的情况
// 为防止p[j]重复统计 所以每次都需要清空map
for (int j = i + 1; j <= n; j++) {
if (sgn(p[i] ^ p[j]) == 0)continue;//两向量叉乘判断三点是否共线
cir = Circle({0, 0}, p[i], p[j]);
mp[cir.p]++;//统计p[j]
res = max(res, mp[cir.p] + 1);//再加上p[i]
}
}
printf("%d\n", res);
return 0;
}
找底层叶子节点,根据叶子节点的排布分成左右两个部分,按照位置配对,让 v i v_{i} vi 与 v m i d + i v_{mid+i} vmid+i配对
#include
using namespace std;
const int N = 1e6 + 10;
int n, m, k;
vector<int> v;
vector<int> e[N];
void dfs(int u, int fa) {
if (e[u].size() == 1) v.push_back(u);
for (int i = 0, sz = e[u].size(); i < sz; i++) {
int v = e[u][i];
if (v != fa) {
dfs(v, u);
}
}
}
int main() {
scanf("%d", &n);
for (int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
int sz = v.size();
if (sz & 1) {
v.push_back(v.back());
sz++;
}
printf("%d\n", sz / 2);
int mid = sz / 2;
for (int i = 0; i < mid; i++) {
printf("%d %d\n", v[i], v[mid + i]);
}
return 0;
}
#include
using namespace std;
int a, b, c;
int main() {
scanf("%2d:%2d:%2d", &a, &b, &c);
int t1 = a * 3600 + b * 60 + c;
scanf("%2d:%2d:%2d", &a, &b, &c);
int t2 = a * 3600 + b * 60 + c;
printf("%d\n", abs(t2 - t1));
return 0;
}
E Exclusive OR
二维优先队列
有点卡时间
#include
using namespace std;
#define N 5005
typedef long long ll;
int head1, tail1;
int qMax[N][N], matrix[N][N];
struct node {
ll v;
int id;
} q1[N];
void addMax(int v, int id) { //维护队列最大值
while (head1 < tail1 && q1[tail1 - 1].v <= v) tail1--;
q1[tail1].v = v;
q1[tail1++].id = id;
}
int getMaxNum(int id) {
while (head1 < tail1 && q1[head1].id < id) head1++;
return q1[head1].v;
}
int main() {
int n, m, k, i, j;
scanf("%d%d%d", &n, &m, &k);
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
matrix[i][j] = i * j / __gcd(i, j);
ll res = 0;
for (j = 1; j <= m; ++j) {
head1 = tail1 = 0;
for (i = 1; i <= k - 1; ++i) {
addMax(matrix[i][j], i);
}
for (i = k; i <= n; ++i) {
addMax(matrix[i][j], i);
qMax[i - k + 1][j] = getMaxNum(i - k + 1);
}
}
for (i = 1; i <= n - k + 1; ++i) {
head1 = tail1 = 0;
for (j = 1; j <= k - 1; ++j) {
addMax(qMax[i][j], j);
}
for (j = k; j <= m; ++j) {
addMax(qMax[i][j], j);
res += getMaxNum(j - k + 1);
}
}
printf("%lld\n", res);
return 0;
}
G Greater and Greater
H Happy Triangle
I Interval
终于看懂的一个博客 → 置换(群)&(J Just Shuffle)
假设原数组 a a a,经过置换 P P P,得到新的数组 c c c
如果只是求 B ( 1 , 2 , 3.... n ) B(1,2,3....n) B(1,2,3....n) 经过一次置换 P P P 得到 A A A,是非常容易的,用公式表示
B 1 = A B^1=A B1=A
但实际上题目要求的是 B B B经过k次置换 P P P 得到 A A A,即
B k = A B^k=A Bk=A
这就需要借助置换 P P P 自身的变换,假设置换 P P P 往后挪了 i n v inv inv 次,根据前面的第3个知识点,上面的公式等同于
B k i n v = A B^{\frac{k}{inv}}=A Binvk=A
现在,只要让 k i n v = 1 \cfrac{k}{inv}=1 invk=1,就能根据得到的 P P P 往前挪 i n v inv inv 次得到原本的置换
而在模意义下,显然 i n v inv inv 是 k k k 的逆元
同时,因为置换 P P P 的一组循环可能无法包含所有的位置,需要分别求出每一组循环的置换 P ′ P' P′ 以及对应的 i n v inv inv,最后结合起来才是答案
数据水了所以没有无解?
据说是原题来着…ICPC NEAU Programming Contest 2020 E. 随便置换(模拟枚举环,置换)
#include
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int a[N];
int n, k;
int res[N];
int vis[N];
vector<int> p;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
int now = i;
p.clear();
//找到每一组循环里的所有位置
while (!vis[now]) {
p.push_back(now);
vis[now] = 1;
now = a[now];// 置换公式代入 a[i]=p[b[i]] ∵b[i]=i ∴ a[i]=p[i]
}
int sz = p.size();
int inv = 0;// 如果用费马小定理求逆元 可能存在模数为负数的情况
while (inv < sz) {
if ((ll) inv * k % sz == 1) break; // 找到一种inv即可
inv++;
}
for (int i = 0; i < sz; i++) {
res[p[i]] = p[(i + inv) % sz]; // 将原来的序列挪位
}
}
}
for (int i = 1; i <= n; i++) {
cout << res[i] << " ";
}
return 0;
}
K Keyboard Free