Fibonacci Sum-HDU 6755
传送门
思路
- 对着式子瞎弄一顿,最后把通项公式套进去发现 a n s = ∑ i = 0 n 1 5 × [ ( 1 + 5 2 ) i c − ( 1 − 5 2 ) i c ] k = 1 5 ∑ i = 0 n ( [ 1 + 5 2 ] i c + [ − ( 1 − 5 2 ) i c ] ) k ans=\sum_{i=0}^{n}\frac{1}{\sqrt{5}}×[(\frac{1+\sqrt{5}}{2})^{ic}-(\frac{1-\sqrt{5}}{2})^{ic}]^k\\=\frac{1}{\sqrt{5}}\sum_{i=0}^{n}([\frac{1+\sqrt{5}}{2}]^{ic}+[-(\frac{1-\sqrt{5}}{2})^{ic}])^k ans=i=0∑n51×[(21+5)ic−(21−5)ic]k=51i=0∑n([21+5]ic+[−(21−5)ic])k然后对这个式子进行二项式定理拆分设 A = ( 1 + 5 2 ) c , B = ( 1 − 5 2 ) c A=(\frac{1+\sqrt{5}}{2})^{c},B=(\frac{1-\sqrt{5}}{2})^{c} A=(21+5)c,B=(21−5)c a n s = 1 5 ∑ i = 0 n [ A i + ( − B i ) ] k = 1 5 ∑ i = 0 n ∑ j = 0 k C k j A i j ( − 1 ) k − j B i ( k − j ) 更 换 和 式 位 置 = 1 5 ∑ j = 0 k C k j ( − 1 ) k − j ∑ i = 0 n A j i B ( k − j ) i ans=\frac{1}{\sqrt{5}}\sum_{i=0}^{n}[A^{i}+(-B^i)]^k\\=\frac{1}{\sqrt{5}}\sum_{i=0}^{n}\sum_{j=0}^{k}C^j_kA^{ij}(-1)^{k-j}B^{i(k-j)}\\更换和式位置=\frac{1}{\sqrt{5}}\sum_{j=0}^{k}C^j_k(-1)^{k-j}\sum_{i=0}^{n}A^{ji}B^{(k-j)i} ans=51i=0∑n[Ai+(−Bi)]k=51i=0∑nj=0∑kCkjAij(−1)k−jBi(k−j)更换和式位置=51j=0∑kCkj(−1)k−ji=0∑nAjiB(k−j)i不难发现更换和式后那是一个普通的等比数列,而且按照时间来算的话枚举k个数是十分合理的,正解公式就出来了
- 这题的时间卡得很紧,对计算组合数求模需要谨慎,另外对每一个k的 A j , B k − j 的 计 算 需 要 O ( 1 ) 处 理 , 避 免 超 时 A^j,B^{k-j}的计算需要O(1)处理,避免超时 Aj,Bk−j的计算需要O(1)处理,避免超时
- 在跑代码前先另写个代码跑出 5 \sqrt{5} 5在 m o d mod mod下的数字 383008016 383008016 383008016, 1 5 \frac{1}{\sqrt{5}} 51在 m o d mod mod的数字 276601605 276601605 276601605
- 然后跑出 A = ( 1 + 5 2 ) c = 691504013 , B = ( 1 − 5 2 ) c = 308495997 A=(\frac{1+\sqrt{5}}{2})^{c}=691504013,B=(\frac{1-\sqrt{5}}{2})^{c}=308495997 A=(21+5)c=691504013,B=(21−5)c=308495997
- 剩下的按公式来算即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int mod = 1e9 + 9;//383008016,276601605
const int N = 1e5 + 10;
const ll Num = 276601605;
ll fp(ll x, ll y) {
ll ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
ll fac[N], ifac[N];
ll C(int n, int k) {
ll ans = fac[n] * ifac[n - k] % mod * ifac[k] % mod;
return ans;
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
fac[0] = 1;
for (int i = 1; i <= 1e5; ++i) fac[i] = fac[i - 1] * i % mod;
ifac[N - 10] = fp(fac[N - 10], mod - 2);
for (int i = 1e5 - 1; i >= 0; --i) ifac[i] = (i + 1) * ifac[i + 1] % mod;
int T; read(T);
const ll _A = 691504013, _B = 308495997;
while (T--) {
ll n, c; int k;
read(n); read(c); read(k);
c = c % (mod - 1);
const ll A = fp(_A, c), B = fp(_B, c);
ll a = 1, b = fp(B, k);
ll _b = fp(B, mod - 2);
ll ans = 0;
for (int j = 0; j <= k; ++j) {
ll tmp = a * b % mod;
ll Sn;
if (tmp == 1) {
Sn = (n + 1) % mod;
}
else {
Sn = (fp(tmp, (n + 1) % (mod - 1)) - 1 + mod) % mod * fp((tmp - 1 + mod) % mod, mod - 2) % mod;
}
if ((k - j) & 1) {
ans = ((ans - Sn * C(k, j) % mod) % mod + mod) % mod;
}
else {
ans = (ans + Sn * C(k, j) % mod) % mod;
}
a = a * A % mod;
b = b * _b % mod;
}
ans = ans * fp(Num, k) % mod;
printf("%lld\n", ans);
}
return 0;
}
Leading Robots-HDU 6759
传送门
思路
- 这道题意思是给定加速度与初始位置,判断有多少人有机会拿到第一名
- 非常经典的单调栈问题,首先根据加速度进行从小到大排序,加速度相同就按照位移从小到大排序,原因下文会阐述
- 首先将排好的第一个压入栈中,进行下一个操作时,先进行第一步判断:设栈顶的 r o b o t P robot\ P robot P,此刻判断的 r o b o t Q robot\ Q robot Q,已知 a P < = a Q a_P<=a_Q aP<=aQ,如果位置 S P < = S Q S_P<=S_Q SP<=SQ,直接可以判断Q点先于P点拿到第一名并且P点不可能超过Q点,所以直接将P点排除
- 当栈内已经有两个元素时,假设栈顶起第二个元素 r o b o t X robot\ X robot X,第一个元素 r o b o t Y robot\ Y robot Y,此刻判断的 r o b o t Z robot\ Z robot Z,设 Y Y Y追上 X X X所需的时间为 t 1 t_1 t1,(由上一个判断可知 Y Y Y在 X X X之后), Z Z Z追上 Y Y Y的时间 t 2 t_2 t2,可知只有当 t 1 < t 2 t1<t2 t1<t2, Y Y Y才有可能拿到第一名,否则将 Y Y Y排除
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 5e4 + 10;
struct node {
int p, a;
bool friend operator<(const node& a, const node& b) {
if (a.a == b.a) return a.p < b.p;
return a.a < b.a;
}
}a[N];
bool check(int x, int y, int z) {
return 1LL * (a[x].p - a[y].p) * (a[z].a - a[y].a) >= 1LL * (a[y].a - a[x].a) * (a[y].p - a[z].p);
}
map<pii, int>vis;
int stk[N];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T; read(T);
while (T--) {
vis.clear();
int n; read(n);
for (int i = 1; i <= n; ++i) {
read(a[i].p); read(a[i].a);
++vis[{
a[i].a, a[i].p}];
}
sort(a + 1, a + 1 + n);
int top = 0;
for (int i = 1; i <= n; ++i) {
while (top && (a[stk[top - 1]].p <= a[i].p) || top > 1 && check(stk[top - 2], stk[top - 1], i)) --top;
stk[top++] = i;
}
int ans = top;
for (int i = 0; i < top; ++i) {
if (vis[{
a[stk[i]].a, a[stk[i]].p}] > 1) --ans;
}
printf("%d\n", ans);
}
return 0;
}
Finding a MEX-HDU 6756
传送门
思路
- 如果对所有点使用线段树、树状数组,更新的复杂度 d l o g ( d ) dlog(d) dlog(d),d为该点的度数,查询则是 l o g d logd logd,对于 q u e r y = 1 0 5 query=10^5 query=105,更新的复杂度还是太高了
- 最直接的就是采用分块的思想,将度数大的点建立线段树进行区间更新,小的数直接暴力枚举,此时大度数的更新 n l o g n \sqrt{n}logn nlogn,查询 l o g n logn logn,小点的更新 O ( 1 ) O(1) O(1),查询最多 n \sqrt{n} n,此时的复杂度可以接受
- 使用线段树写的较慢,用了一大堆优化结果 2808 m s 2808ms 2808ms卡着时间过…
- 下面是线段树的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
struct node {
int l, r, num;
}tree[320][N << 2];
inline void pushUp(int rt, int op) {
tree[op][rt].num = tree[op][rt << 1].num + tree[op][rt << 1 | 1].num;
}
int vis[320][N];
void build(int l, int r, int rt, int op) {
tree[op][rt].l = l;
tree[op][rt].r = r;
if (l == r) {
tree[op][rt].num = vis[op][l] ? 1 : 0;
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1, op);
build(mid + 1, r, rt << 1 | 1, op);
pushUp(rt, op);
}
void update(int ql, int qr, int rt, int op) {
if (ql == tree[op][rt].l && tree[op][rt].r == qr) {
tree[op][rt].num = vis[op][ql] > 0 ? 1 : 0;
return;
}
int mid = (tree[op][rt].l + tree[op][rt].r) >> 1;
if (ql <= mid) update(ql, qr, rt << 1, op);
else if (mid < qr) update(ql, qr, rt << 1 | 1, op);
pushUp(rt, op);
}
ll query(int rt, int op) {
if (tree[op][rt].l == tree[op][rt].r) return tree[op][rt].l;
if (tree[op][rt << 1].num != (tree[op][rt << 1].r - tree[op][rt << 1].l + 1)) return query(rt << 1, op);
else return query(rt << 1 | 1, op);
}
int a[N], mark[N], tot, d[N];
vector<int>vec[N], G[N];
bool s[N];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T; read(T);
while (T--) {
int n, m; read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= n; ++i) vec[i].clear(), G[i].clear();
tot = 0;
for (int i = 1; i <= m; ++i) {
int u, v;
read(u); read(v);
vec[u].emplace_back(v);
vec[v].emplace_back(u);
}
int block = sqrt(n);
for (int i = 1; i <= n; ++i) {
if ((d[i] = vec[i].size()) >= block) {
mark[i] = ++tot;
for (int j = 0; j < d[i]; ++j) vis[tot][j] = 0;
for (auto v : vec[i]) {
G[v].emplace_back(i);
if (a[v] < d[i]) ++vis[mark[i]][a[v]];
}
build(0, d[i] - 1, 1, mark[i]);
}
}
int Q; read(Q);
while (Q--) {
int op; read(op);
if (op == 1) {
int x, y; read(x), read(y);
for (auto v : G[x]) {
if (a[x] < d[v] && !(--vis[mark[v]][a[x]])) update(a[x], a[x], 1, mark[v]);
if (y < d[v] && (++vis[mark[v]][y]) == 1) update(y, y, 1, mark[v]);
}
a[x] = y;
}
else {
int x; read(x);
if (d[x] < block) {
for (int i = 0; i < d[x]; ++i) s[i] = 0;
for (auto v : vec[x]) {
if (a[v] >= d[x]) continue;
s[a[v]] = 1;
}
for (int i = 0;; ++i) {
if (!s[i]) {
printf("%d\n", i);
break;
}
}
}
else {
printf("%d\n", query(1, mark[x]));
}
}
}
}
return 0;
}
- 使用树状数组快了整整 1 s 1s 1s,惊了
线段树是弟弟
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
#define lowbit(x) ((x)&-(x))
int sum[320][N];
void add(int x, int d, int n, int op) {
while (x <= n) {
sum[op][x] += d;
x += lowbit(x);
}
}
bool check(int x, int op) {
int ans = 0;
int tmp = x;
while (x > 0) {
if (sum[op][x] != lowbit(x)) return 1;
ans += sum[op][x];
x -= lowbit(x);
}
return ans != tmp;
}
int vis[320][N];
int query(int op, int n) {
if (!vis[op][0]) return 0;
int l = 1, r = n, ans = n;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid, op)) {
r = mid - 1;
ans = mid;
}
else l = mid + 1;
}
return ans;
}
int a[N], mark[N], d[N], tot;
vector<int>vec[N], G[N];
bool s[N];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T; read(T);
while (T--) {
int n, m; read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= n; ++i) vec[i].clear(), G[i].clear();
tot = 0;
while (m--) {
int u, v;
read(u); read(v);
vec[u].emplace_back(v);
vec[v].emplace_back(u);
}
int block = sqrt(n);
for (int i = 1; i <= n; ++i) {
if ((d[i] = vec[i].size()) >= block) {
mark[i] = ++tot;
for (int j = 0; j <= d[i]; ++j) vis[tot][j] = sum[tot][j] = 0;
for (auto v : vec[i]) {
G[v].emplace_back(i);
if (a[v] <= d[i]) {
if ((++vis[mark[i]][a[v]]) == 1 && a[v]) add(a[v], 1, d[i], mark[i]);
}
}
}
}
int Q; read(Q);
while (Q--) {
int op; read(op);
if (op == 1) {
int x, y; read(x), read(y);
for (auto v : G[x]) {
if (a[x] <= d[v] && !(--vis[mark[v]][a[x]]) && a[x]) add(a[x], -1, d[v], mark[v]);
if (y <= d[v] && (++vis[mark[v]][y]) == 1 && y) add(y, 1, d[v], mark[v]);
}
a[x] = y;
}
else {
int x; read(x);
if (d[x] < block) {
for (int i = 0; i < d[x]; ++i) s[i] = 0;
for (auto v : vec[x]) {
if (a[v] >= d[x]) continue;
s[a[v]] = 1;
}
for (int i = 0;; ++i) {
if (!s[i]) {
printf("%d\n", i);
break;
}
}
}
else {
printf("%d\n", query(mark[x], d[x]));
}
}
}
}
return 0;
}
Lead of Wisdom
传送门
思路
- 一开始的时候没往暴力搜索去想,一直在用数学方法写,一直WA,看到别人做得那么快直接怀疑人生
- 这道题的数据比较小,50个点稍微剪下枝就可以过了,设当前搜过的答案中最大值是 a n s ans ans,先预处理一遍对于每一个 t y p e type type,对应的每个 A , B , C , D A,B,C,D A,B,C,D的最大值都存下来,即使不是同一件,因为当搜到 k k k 层时,若前面的 t y p e type type的和加上包括 k k k 以后的所有的最大值的和,计算出来的结果都比这个 a n s ans ans小的话,那就没必要再搜了,不是同一件的都小了,更别说匹配到一件的 A , B , C , D A,B,C,D A,B,C,D了,这样的剪枝还是有效的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
int a[55][55][4], sz[55];
int maxn[55][4];
int n, k;
ll ans;
ll A, B, C, D;
void dfs(int x) {
if (x == 0) {
ans = max(ans, (100LL + A) * (100LL + B) * (100LL + C) * (100LL + D));
return;
}
if (ans > (100LL + A + maxn[x][0]) * (100LL + B + maxn[x][1]) * (100LL + C + maxn[x][2]) * (100LL + D + maxn[x][3])) return;
if (sz[x] == 0) dfs(x - 1);
for (int i = 1; i <= sz[x]; ++i) {
A += a[x][i][0];
B += a[x][i][1];
C += a[x][i][2];
D += a[x][i][3];
dfs(x - 1);
A -= a[x][i][0];
B -= a[x][i][1];
C -= a[x][i][2];
D -= a[x][i][3];
}
}
int main() {
int T; read(T);
for (int i = 0; i < 4; ++i) maxn[0][i] = 0;
while (T--) {
memset(sz, 0, sizeof sz);
read(n); read(k);
for (int i = 1; i <= n; ++i) {
int t; read(t);
++sz[t];
for (int j = 0; j < 4; ++j) {
int tmp; read(tmp);
a[t][sz[t]][j] = tmp;
}
}
for (int i = 1; i <= k; ++i) {
for (int j = 0; j < 4; ++j) {
int tmp = 0;
for (int k = 1; k <= sz[i]; ++k) {
tmp = max(tmp, a[i][k][j]);
}
maxn[i][j] = maxn[i - 1][j] + tmp;
}
}
ans = A = B = C = D = 0;
dfs(k);
printf("%lld\n", ans);
}
return 0;
}
最后跑出来的时间还挺优秀
The Oculus-HDU 6768
传送门
思路
- 一开始被这题的数据给劝退…那么大长度的fibo第一反应直接爆整型
- 后来才意识到,这道考点有点类似hash的考法,用unsigned long long存数会进行溢出取余,将 1 e 6 1e6 1e6长度的fibo数跑了一遍,还真就没有冲撞的
- 知道这些后就是水题一道
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 2e6 + 10;
ull fibo[N];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
fibo[1] = 1, fibo[2] = 2;
for (int i = 3; i <= 2e6; ++i) fibo[i] = fibo[i - 1] + fibo[i - 2];
int T; read(T);
while (T--) {
int t; read(t);
ull A = 0, B = 0, C = 0;
for (int i = 1; i <= t; ++i) {
int x; read(x);
if (x) A += fibo[i];
}
read(t);
for (int i = 1; i <= t; ++i) {
int x; read(x);
if (x) B += fibo[i];
}
read(t);
for (int i = 1; i <= t; ++i) {
int x; read(x);
if (x) C += fibo[i];
}
C = A * B - C;
for (int i = 1;; ++i) {
if (fibo[i] == C) {
printf("%d\n", i);
break;
}
}
}
return 0;
}
Total Eclipse-HDU 6763
传送门
思路
- 首先按照正常的想法去想,先把一个连通块里面权值最小的作为 k k k,进行 k k k次操作,操作完后会分成至少2个的子联通分块,继续进行相同操作直到所有点删光
- 但上面的操作的 复杂度是 O ( n 2 ) O(n^2) O(n2),不够优秀,先考虑另一种思路,将复杂度降至 O ( n ) , O(n), O(n),这份大佬的博客写得很详细
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
int head[N], cnt;
struct edge {
int next, to;
}e[M << 1];
void add(int u, int v) {
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt++;
}
struct node {
int id, x;
}a[N];
int fa[N]; bool vis[N];
int get(int x) {
if (x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T; read(T);
while (T--) {
int n, m; read(n), read(m);
memset(head, -1, sizeof head); cnt = 0;
memset(vis, false, sizeof vis);
for (int i = 1; i <= n; ++i) {
read(a[i].x);
a[i].id = i;
}
for (int i = 1; i <= m; ++i) {
int u, v; read(u), read(v);
add(u, v);
add(v, u);
}
sort(a + 1, a + 1 + n, [](const node& a, const node& b) {
return a.x > b.x;
});
for (int i = 1; i <= n; ++i) {
fa[i] = i;
}
ll tot = 0, ans = 0; a[n + 1].x = 0;
for (int i = 1; i <= n; ++i) {
++tot;
int u = a[i].id;
vis[u] = 1;
int fx = get(u);
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (vis[v]) {
int fy = get(v);
if (fx != fy) {
fa[fy] = fx;
--tot;
}
}
}
ans += tot * (a[i].x - a[i + 1].x);
}
printf("%lld\n", ans);
}
return 0;
}
Tokitsukaze and Multiple-HDU 6794
传送门
思路
- 先将n以内所有的前缀和 s u m [ i ] % p sum[i] \%p sum[i]%p求出来,后面利用前缀和的思想,若 s u m [ i ] sum[i] sum[i]在 i i i以前出现过一次,则两者的差值必定是 p p p的倍数
- 另外如果上一个 s u m [ i ] sum[i] sum[i]与 i i i之间以经存在一个 p p p的倍数,这个 i i i就不算在内
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 1e5 + 10;
int a[N], sum[N];
int vis[N];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T; read(T);
while (T--) {
int n, p; read(n), read(p);
sum[0] = 0;
for (int i = 1; i <= n; ++i) {
read(a[i]);
a[i] %= p;
sum[i] = (sum[i - 1] + a[i]) % p;
}
for (int i = 1; i <= p; ++i) vis[i] = -1;
int ans = 0, pos = 0;
vis[0] = 0;
for (int i = 1; i <= n; ++i) {
if (vis[sum[i]] >= pos) {
++ans;
pos = i;
}
vis[sum[i]] = i;
}
printf("%d\n", ans);
}
return 0;
}
Little W and Contest -HDU 6795
传送门
思路
- 设 c o d e r 总 数 t o t c , r e a d e r 总 数 t o t r coder总数totc,reader总数totr coder总数totc,reader总数totr
- 首先求出一开始的 a n s = C t o t r 1 C t o t c 2 + C t o t c 3 ans=C^1_{totr}C^2_{totc}+C^3_{totc} ans=Ctotr1Ctotc2+Ctotc3
- 首先根据每个点是 r e a d e r o r c o d e r reader\ or\ coder reader or coder,确定初始 n u m c [ i ] , n u m r [ i ] numc[i],numr[i] numc[i],numr[i]
- 对于 n − 1 n-1 n−1条边的处理,利用并查集的思想,共分为4种情况
- C r ( x ) 1 ∗ C c ( y ) 1 ∗ C t o t c − c ( x ) − c ( y ) 1 C_{r(x)}^1*C_{c(y)}^1*C_{totc-c(x)-c(y)}^1 Cr(x)1∗Cc(y)1∗Ctotc−c(x)−c(y)1
- C r ( y ) 1 ∗ C c ( x ) 1 ∗ C t o t c − c ( x ) − c ( y ) 1 C_{r(y)}^1*C_{c(x)}^1*C_{totc-c(x)-c(y)}^1 Cr(y)1∗Cc(x)1∗Ctotc−c(x)−c(y)1
- C c ( x ) 1 ∗ C c ( y ) 1 ∗ C t o t c − c ( x ) − c ( y ) 1 C_{c(x)}^1*C_{c(y)}^1*C_{totc-c(x)-c(y)}^1 Cc(x)1∗Cc(y)1∗Ctotc−c(x)−c(y)1
- C t o t r − r ( x ) − r ( y ) 1 ∗ C c ( x ) 1 ∗ C c ( y ) 1 C_{totr-r(x)-r(y)}^1*C_{c(x)}^1*C_{c(y)}^1 Ctotr−r(x)−r(y)1∗Cc(x)1∗Cc(y)1
- 处理完后将 x , y x,y x,y合并,并累加对应的 c , r c,r c,r,详情看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
inline ll C(ll n, int k) {
if (n < k) return 0;
if (k == 1) {
return n;
}
if (k == 2) {
return n * (n - 1) / 2 % mod;
}
return n * (n - 1) * (n - 2) / 2 / 3 % mod;
}
int fa[N];
int get(int x) {
if (x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
int totc, totr, a[N], numc[N], numr[N];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T; read(T);
while (T--) {
int n; read(n);
totc = totr = 0;
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= n; ++i) {
read(a[i]);
if (a[i] & 1) ++totr, numr[i] = 1, numc[i] = 0;
else ++totc, numc[i] = 1, numr[i] = 0;
}
ll ans = (C(totr, 1) * C(totc, 2) % mod + C(totc, 3)) % mod;
printf("%lld\n", ans);
for (int i = 1; i < n; ++i) {
int x, y; read(x), read(y);
int fx = get(x), fy = get(y);
if (fx != fy) {
ans = (ans - C(numr[fx], 1) * C(numc[fy], 1) % mod * C(totc - numc[fx] - numc[fy], 1) % mod + mod) % mod;
ans = (ans - C(numr[fy], 1) * C(numc[fx], 1) % mod * C(totc - numc[fx] - numc[fy], 1) % mod + mod) % mod;
ans = (ans - C(numc[fx], 1) * C(numc[fy], 1) % mod * C(totc - numc[fx] - numc[fy], 1) % mod + mod) % mod;
ans = (ans - C(numc[fx], 1) * C(numc[fy], 1) % mod * C(totr - numr[fx] - numr[fy], 1) % mod + mod) % mod;
numc[fx] += numc[fy];
numr[fx] += numr[fy];
fa[fy] = fx;
}
printf("%lld\n", ans);
}
}
return 0;
}
其实这道题最大的数据也不会爆 l o n g l o n g long\ long long long,所以可以在输出的时候再进行mod操作也是可行的
Parentheses Matching-HDU 6799
传送门
思路
- 对于这种经典的括号匹配,首先容易想到用单调栈来匹配左括号与右括号,这题还有额外的条件,按照字典序输出括号串,所以用栈来存左括号,使用双向队列来存 * ,需要左括号进行匹配就从前面提取,需要匹配右括号就从后面提取,保证答案为最小字典序
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 1e5 + 10;
char s[N];
int len;
bool check() {
len = strlen(s);
deque<int>que;
stack<int>stk;
for (int i = 0; i < len; ++i) {
if (s[i] == '*') {
que.push_back(i);
}
else if (s[i] == '(') stk.push(i);
else if (s[i] == ')') {
if (!stk.empty()) {
stk.pop();
}
else if (!que.empty()) {
s[que.front()] = '(';
que.pop_front();
}
else return false;
}
}
if (stk.size() > que.size()) return false;
while (!stk.empty() && !que.empty()) {
if (que.back() < stk.top()) return false;
s[que.back()] = ')';
que.pop_back();
stk.pop();
}
return stk.empty();
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T; read(T);
while (T--) {
scanf("%s", s);
if (check()) {
for (int i = 0; i < len; ++i) {
if (s[i] == '(' || s[i] == ')') printf("%c", s[i]);
}
puts("");
}
else puts("No solution!");
}
return 0;
}