今天队友不仅又爆零了,还送我几发罚时(下次再这样就要请客谢罪了)
A digits 2
解法:算出n每为数字的和x之后,算出x与n的lcm,输出lcm / x 个n即可
#include
using namespace std;
int gao(int x) {
if (!x)
return 0;
return x % 10 + gao(x / 10);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
cin>>n;
int x = gao(n);
int d = __gcd(x, n);
int lcm = n * x / d;
//printf("lcm = %d\n", lcm);
int k = lcm / x;
for (int i = 1; i <= k; i++)
printf("%d", n);
puts("");
}
}
B generator 1
首先先构造矩阵,然后跑十进制的矩阵快速幂即可,水题
#include
#define ll long long
using namespace std;
const int N = 2;
ll tmp[N][N], mod;
void add(ll &x, ll y) {
x += y;
if (x >= mod)
x -= mod;
}
void multi(ll a[][N],ll b[][N],int n)
{
memset(tmp,0,sizeof tmp);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
for(int k=0;k<n;k++)
add(tmp[i][j], a[i][k]*b[k][j] % mod);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
a[i][j]=tmp[i][j];
}
ll res[N][N];
int len;
const int maxn = 1e6 + 10;
char s[maxn];
void Pow(ll a[][N])
{
memset(res,0,sizeof res);
for(int i=0;i<N;i++) res[i][i]=1;
for (int i = len; i; i--)
{
int m = s[i] - '0';
for (int j = 1; j <= m; j++)
multi(res, a, N);
ll tmp2[2][2];
tmp2[0][0] = a[0][0];
tmp2[0][1] = a[0][1];
tmp2[1][0] = a[1][0];
tmp2[1][1] = a[1][1];
for (int j = 1; j <= 3; j++)
multi(a, a, N);
multi(a, tmp2, N);
multi(a, tmp2, N);
}
}
void gao() {
int p = len;
while (s[p] == '0')
s[p] = '9', p--;
s[p]--;
}
int main() {
int x0, x1, a, b;
scanf("%d%d%d%d", &x0, &x1, &a, &b);
scanf("%s%d", s + 1, &mod);
len = strlen(s + 1);
gao();
ll A[2][2] = {a, b, 1, 0};
Pow(A);
ll ans = res[0][0] * x1 % mod + res[0][1] * x0 % mod;
printf("%lld\n", ans % mod);
}
C generator 2
带公式的题解都在这儿
E independent set 1
题意:给一个无向图,设图的权值为该图中最多的独立点(即任意两点无边连接)的数量,求题目给的无向图的所有子图的总权值和
解法:我靠巨水,要是放弃F题就好了,设d[i]为子图点集为 i (二进制下),的最多独立点数量,对于d[i],我们找到 i 的最右边1的位置k(其他的1转移也可以,考虑最右边是因为这样复杂度小),如果我们不加这个点d[i] = max(d[i], d[i ^ (1 << k)]),假设G[k]为与 k 点不连边的点集,如果加k点,d[i] = max(d[i], (i & G[k]) + 1)
#include
#define ll long long
using namespace std;
int G[26];
char d[1 << 26];
int main() {
int n, m, u, v;
cin>>n>>m;
while (m--) {
cin>>u>>v;
G[u] |= (1 << v);
G[v] |= (1 << u);
}
int ans = 0, s = (1 << n) - 1;
for (int i = 0; i < n; i++)
G[i] = (s ^ (G[i] | (1 << i)));
for (int i = 1; i < (1 << n); i++) {
int x;
for (x = 0; ; x++)
if (i >> x & 1)
break;
d[i] = max(d[i ^ (1 << x)], char(d[i & G[x]] + 1));
ans += d[i];
}
cout<<ans;
}
F maximum clique 1
当时知道就是个二分图求最大独立集方案,但是写不出罚座一下午。首先我们把任意两个只有一个bit不同的数连边(说白了就是二进制下1的个数只相差1),可以证明一下该图肯定是二分图,也就是没有奇环:对于一个起点o,假设它存在于一个奇环中,那么直接与o相连的两个数x,y的1(二进制下)的个数要么相同要么相差2(即偶数),假设环的长度是3显然x,y不应该有边相连,与假设矛盾,延伸到任意奇数长度环,最后两个相连的数1的个数相差都为偶数,与假设矛盾。因此我们直接套二分图最大独立集模板就行
#include
using namespace std;
const int maxn = 5010;
int a[maxn], b[maxn], n, col[maxn], Left[maxn], Right[maxn], vis[maxn];
vector<int> G[maxn], ans, tmp;
void dfs(int u, int color) {
tmp.push_back(u);
col[u] = color;
for (auto v : G[u])
if (!col[v])
dfs(v, 3 - color);
}
int dfs2(int u) {
vis[u] = 1;
for (auto v : G[u])
if (!vis[v]) {
vis[v] = 1;
if (!Left[v] || dfs2(Left[v])) {
Left[v] = u;
Right[u] = v;
return 1;
}
}
return 0;
}
void gao() {
int color = 2;
for (auto u : tmp)
if (col[u] == color) {
for (auto v : tmp)
vis[v] = 0;
dfs2(u);
}
for (auto u : tmp)
vis[u] = 0;
for (auto u : tmp)
if (col[u] == color && !Right[u])
dfs2(u);
for (auto u : tmp)
if ((col[u] == color && vis[u]) || (col[u] == 3 - color && !vis[u]))
ans.push_back(b[u]);
}
int main() {
cin>>n;
for (int i = 1; i <= n; i++)
cin>>a[i], b[i] = a[i];
sort(b + 1, b + 1 + n);
for (int i = 1; i <= n; i++)
a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;
for (int i = 1; i <= n; i++)
for (int j = 0; j < 30; j++) {
int x = b[i] ^ (1 << j);
int k = lower_bound(b + 1, b + 1 + n, x) - b;
if (b[k] == x)
G[i].push_back(k);
}
for (int i = 1; i <= n; i++) {
if (col[i])
continue;
tmp.clear();
dfs(i, 1);
gao();
}
printf("%d\n", ans.size());
for (int i = 0; i < ans.size(); i++)
printf("%d%c", ans[i], (i == ans.size() - 1) ? '\n' : ' ');
}
G subsequence 1
解法:首先从 s 串中选的数字长度大于 t 串长度,肯定ok,那么我们枚举第一个数字的位置并且用组合数搞一搞就可以了,接下来我们用dp搞长度与 t 串相等且大于 t 的数字数量即可,设d[i][j]为 s 串前 i 个数字中,选了 j 个数字且大于 t 串的前 j 个数组的方案数,f[i][j]为与 t 串相等的方案数,对于 s 串第 i 个数字,我不选 :d[i][j] = d[i - 1][j],f[i][j] = f[i - 1][j],选:d[i][j] += d[i - 1][j - 1],如果s[i] > t[j],那么d[i][j] += f[i - 1][j - 1],如果s[i] = t[j],那么f[i][j] += f[i - 1][j - 1]
#include
#define ll long long
using namespace std;
const int maxn = 3005, mod = 998244353;
int d[maxn][maxn], c[maxn][maxn], f[maxn][maxn];
char s[maxn], t[maxn];
void add(int &x, int y) {
x += y;
if (x >= mod)
x -= mod;
if (x < 0)
x += mod;
}
int main() {
int T;
scanf("%d", &T);
c[0][0] = 1;
for (int i = 1; i <= 3000; i++) {
c[i][0] = c[i][i] = 1;
for (int j = 1; j < i; j++)
add(c[i][j], c[i - 1][j - 1] + c[i - 1][j]);
}
for (int i = 1; i <= 3000; i++)
for (int j = i; j >= 0; j--)
add(c[i][j], c[i][j + 1]);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
scanf("%s%s", s + 1, t + 1);
int ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
d[i][j] = f[i][j] = 0;
for (int i = 1; i <= n; i++) {
f[i - 1][0] = 1;
if (s[i] != '0' && (n - i + 1 > m))
add(ans, c[n - i][m]);
for (int j = 1; j <= min(i, m); j++) {
add(f[i][j], f[i - 1][j]);
add(d[i][j], d[i - 1][j]);
add(d[i][j], d[i - 1][j - 1]);
if (s[i] > t[j])
add(d[i][j], f[i - 1][j - 1]);
else if (s[i] == t[j])
add(f[i][j], f[i - 1][j - 1]);
}
}
add(ans, d[n][m]);
printf("%d\n", ans);
}
}
H subsequence 2
解法:我们对每个字母,连有向边到它前面一个字母,然后拓扑排序即可,现场我竟然写了线段树优化拓扑图…线段树优化拓扑图:对于每个字母,对它前面所有字母都连有向边,可以线段树区间+1实现连边操作,然后拓扑排序即可
#include
using namespace std;
const int maxn = 10100;
struct node {
#define ls o * 2
#define rs o * 2 + 1
#define mid (l + r) / 2
int mn[maxn * 4], tag[maxn * 4];
void pushdown(int o) {
tag[ls] += tag[o]; tag[rs] += tag[o];
mn[ls] += tag[o]; mn[rs] += tag[o];
tag[o] = 0;
}
void up(int o, int l, int r, int ql, int qr, int v) {
if (ql > qr)
return;
if (l >= ql && r <= qr) {
mn[o] += v;
tag[o] += v;
return;
}
pushdown(o);
if (ql <= mid)
up(ls, l, mid, ql, qr, v);
if (qr > mid)
up(rs, mid + 1, r, ql, qr, v);
mn[o] = min(mn[ls], mn[rs]);
}
int qu(int o, int l, int r) {
if (mn[o] > 0)
return -1;
if (l == r)
return l;
pushdown(o);
if (mn[ls] == 0)
return qu(ls, l, mid);
return qu(rs, mid + 1, r);
}
} tree[10];
int p[10];
char s[maxn];
stack<char> ans;
#define pi pair
#define mk make_pair
vector<pi> G[10][maxn][2];
int vis[26];
int main() {
int n, m, len;
char str[3];
scanf("%d%d", &n, &m);
int ttt = n;
int T = m * (m - 1) / 2;
while (T--) {
scanf("%s%d", str, &len);
if (len == 0)
continue;
memset(vis, 0, sizeof(vis));
scanf("%s", s + 1);
for (int i = 1; i <= len; i++) {
int x = s[i] - 'a';
++vis[x];
G[x][vis[x]][0].push_back(mk(x, vis[x] - 1));
tree[x].up(1, 1, ttt, 1, vis[x] - 1, 1);
int c;
for (int j = 0; j < 2; j++)
if (str[j] != s[i])
c = str[j] - 'a';
G[x][vis[x]][1].push_back(mk(c, vis[c]));
tree[c].up(1, 1, n, 1, vis[c], 1);
}
int x = str[0] - 'a';
tree[x].up(1, 1, n, vis[x] + 1, n, 1e3);
int y = str[1] - 'a';
tree[y].up(1, 1, n, vis[y] + 1, n, 1e3);
}
while (1) {
int k = -1, c;
for (int i = 0; i < m; i++) {
int x = tree[i].qu(1, 1, ttt);
if (x != -1) {
k = x;
c = i;
break;
}
}
if (k == -1)
break;
ans.push(c + 'a');
for (auto tmp : G[c][k][0])
tree[c].up(1, 1, ttt, 1, tmp.second, -1);
for (auto tmp : G[c][k][1])
tree[tmp.first].up(1, 1, ttt, 1, tmp.second, -1);
tree[c].up(1, 1, n, k, k, 1e8);
}
if (ans.size() != ttt)
puts("-1");
else {
while (!ans.empty())
printf("%c", ans.top()), ans.pop();
}
}
I three points 1
队友博客链接