题解 | 广州大学第十七届ACM大学生程序设计竞赛题解
广州大学第十七届ACM大学生程序设计竞赛题解
很感谢各位参加本次同步赛,由于出题人水平有限,如果体验不好深感抱歉。
A 萤火虫
由于2操作可以将一个数改变成任意值,所以可以交换操作1和操作2的顺序,先进行完操作2再进行操作1,使得序列全0。记,只通过操作1能使得数列全部变成0,当且仅当。
每次操作1相当于对所有加减同一个数,假如一开始,通过若干次操作1贪心把前n-k个数变成0后,剩下k个数每个数对应一个,剩下k个数相等,所以能将其全部变成0。
假如数组每个元素都为0,通过若干次操作1后,仍然满足。
所以只需通过操作2让即可,每次改变一个数就能使变成任意值。 所以答案为
标程:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 10;
int n, k, s[N];
void work() {
for (int i = 0; i < k; ++i)s[i] = 0;
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
int x; cin >> x;
s[i % k] += x;
}
sort(s, s + k);
int len = 0, mxlen = 0;
for (int i = 0; i < k; ++i) {
if (i > 0 && s[i] == s[i - 1])len++;
else {
mxlen = max(mxlen, len);
len = 1;
}
}
mxlen = max(mxlen, len);
cout << k - mxlen << '\n';
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T; cin >> T;
while (T--) {
work();
}
}
B 罗伯特
假如可以在任意时刻加入标志,那么设为到达点,放置过的标志次数为,方向为的最短距离,进行bfs即可。
注意到bfs过程中没有记录之前放置标志的位置,因为如果遇到之前放置的标记,说明走了一个环,走的这段环是没有意义的。
bfs跑出一条最短路,然后把路径上需要放置的标志改成在一开始的时候放置,在路径上从起点到终点移动。发现不会遇到在经过该点之后才放置的标志。因为之后经过该点时就是按标志走,可以直接在第一次经过该点时就按标志走。 时间复杂度,如果用多一个log
标程:
#include<bits/stdc++.h>
using namespace std;
struct State {
int d, x, y, c, f;
bool operator<(const State& b) const {
return d > b.d;
}
};
int dis[110][110][60][4], dx[] = { -1,0,1,0 }, dy[] = { 0,1,0,-1 };
bool vis[110][110][60][4];
char mp[110][110];
pair<int, int>convey[110][110];
void work() {
int n, m, k, p; cin >> n >> m >> k >> p;
while (k--) {
int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
convey[x1][y1] = { x2,y2 };
convey[x2][y2] = { x1,y1 };
}
for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j) {
cin >> mp[i][j];
if (mp[i][j] == 'U')mp[i][j] = '0';
if (mp[i][j] == 'R')mp[i][j] = '1';
if (mp[i][j] == 'D')mp[i][j] = '2';
if (mp[i][j] == 'L')mp[i][j] = '3';
}
for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)for (int k = 0; k <= p; ++k)for (int l = 0; l < 4; ++l)dis[i][j][k][l] = 1e9;
dis[1][1][0][1] = 0;
priority_queue<State>Q;
Q.push({ 0,1,1,0,1 });
while (Q.size()) {
State s = Q.top(); Q.pop();
if (vis[s.x][s.y][s.f][s.c])continue;
vis[s.x][s.y][s.f][s.c] = 1;
if (mp[s.x][s.y] == '.') {
for (int i = 0; i < 4; ++i) {
int nx = s.x + dx[i], ny = s.y + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] != '#') {
int nf = i, nc = s.c + (s.f != i);
if (convey[nx][ny].first != 0) {
auto q = convey[nx][ny];
nx = q.first, ny = q.second;
}
if (nc <= p && dis[nx][ny][nc][nf] > dis[s.x][s.y][s.c][s.f] + 1) {
dis[nx][ny][nc][nf] = dis[s.x][s.y][s.c][s.f] + 1;
Q.push({ dis[nx][ny][nc][nf],nx,ny,nc,nf });
}
}
}
}
if (mp[s.x][s.y] >= '0' && mp[s.x][s.y] <= '3' || mp[s.x][s.y] == '@') {
int i = mp[s.x][s.y] - '0';
if (mp[s.x][s.y] == '@')i = s.f;
int nx = s.x + dx[i], ny = s.y + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] != '#') {
int nf = i, nc = s.c;
if (convey[nx][ny].first != 0) {
auto q = convey[nx][ny];
nx = q.first, ny = q.second;
}
if (nc <= p && dis[nx][ny][nc][nf] > dis[s.x][s.y][s.c][s.f] + 1) {
dis[nx][ny][nc][nf] = dis[s.x][s.y][s.c][s.f] + 1;
Q.push({ dis[nx][ny][nc][nf],nx,ny,nc,nf });
}
}
}
}
int ans = 1e9;
for (int c = 0; c <= p; ++c) {
for (int f = 0; f < 4; ++f) {
ans = min(ans, dis[n][m][c][f]);
}
}
if (ans == 1e9) {
cout << "NO\n"; return;
}
cout << "YES\n" << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
work();
}
C 论AE自动化的重要性
把材料合成画成图,得到一张拓扑图。
个生成一个,从往加边,边权,没有入边说明无法通过合成得到,为合成起点。
bfs或者dfs跑一遍。
标程1:
#include <bits/stdc++.h>
//#include <Windows.h>
#include <iostream>
#include <fstream>
#include <ext/pb_ds/trie_policy.hpp>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
#define inf std::numeric_limits<ll>::max()
//#define in read()
#define pb push_back
//#define out(x) write(x);
using namespace __gnu_pbds;
using namespace std;
const ld PI = acos(-1.0);
#define Please return
#define AC 0
struct _IO
{
_IO()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
} _io;
const ll mod=1e9+7;
ll qpow(ll n,ll k){
ll ans=1;
while(k){
if(k&1){
ans*=n;
ans%=mod;
}
n*=n;
n%=mod;
k>>=1;
}
return ans;
}
const int maxn=1e5+7;
vector<pair<ll,ll>>edge[maxn];
ll v[maxn];
ll d[maxn];
int main()
{
ll n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
edge[i].clear();
v[i]=0;
d[i]=0;
}
for(int i=0;i<k;i++){
ll a,b;
cin>>a>>b;
v[a]+=b;
}
for(int i=0;i<m;i++){
ll x,y,c;
cin>>x>>y>>c;
edge[y].push_back({x,c});
d[x]++;
}
queue<ll> q;
for(int i=1;i<=n;i++){
if(d[i]==0){
q.push(i);
}
}
ll ans=0;
while(!q.empty()){
ll x=q.front();
q.pop();
ans+=v[x];
ans%=mod;
for(auto y:edge[x]){
d[y.first]--;
if(d[y.first]==0){
q.push(y.first);
}
v[y.first]+=v[x]*y.second;
v[y.first]%=mod;
}
}
cout<<ans<<endl;
}
标程2:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
#define Ve(T) vector<T>
#define VVe(T) vector<vector<T>>
const ll mod = 1e9+7;
ll add(ll &x, ll y) { return x = (x + y + mod) % mod; }
ll mul(ll x, ll y) { return (x * y) % mod; }
ll sub(ll x, ll y) { return (x - y + mod) % mod; }
void solve()
{
int n, m, k;
cin >> n >> m >> k;
Ve(pii) v(k);
for(auto &[i, j]:v) cin >> i >> j, i--; // i need js
VVe(pii) e(n);
for(int x,y,c,i=0; i<m; i++) {
cin >> x >> y >> c; x--, y--;
e[y].push_back({x, c}); // x->y cnt==c
}
Ve(ll) dp(n, 0);
function<void(int)> dfs = [&](int u) {
if(dp[u]) return void();
if(e[u].size()==0) return dp[u] = 1, void();
ll res = 1;
for(auto [v, c]:e[u]) dfs(v), add(res, mul(dp[v], c));
dp[u] = res;
};
for(int i=0; i<n; i++) if(!dp[i]) dfs(i);
ll ans = 0;
for(auto [i,j]:v) add(ans, mul(dp[i], j));
cout << ans << '\n';
}
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// cin >> T;
while(T--) {
solve();
}
return 0;
}
D 轻音部的毕业旅行
u和v建边,边权c,得到无向带权图。
首先考虑从x飞往y的贡献,分析题意,这个贡献就是x到y的所有 路径上最大边权 的最小值,即最小瓶颈路上的最大边权。 按克鲁斯卡尔建最小生成树的顺序,求最小生成树链上的最大值,倍增,树剖或者dfs序都可以(标程2)。 或者建克鲁斯卡尔重构树,u和v的lca即为最小瓶颈路上的最大边权(标程1)。
询问先预处理1到n的贡献前缀和,然后每次求差分即可。
标程1:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Edge {
int u, v, c;
};
bool cmp(const Edge &e_l, const Edge &e_r) { return e_l.c < e_r.c; }
struct KruskalTree {
int n, m;
vector<Edge> e;
vector<vector<pair<int,int>>> eg;
vector<int> dsu_fa, lca_dep;
vector<vector<int>> lca_fa, lca_ma;
void init() {
e.resize(m+1); dsu_fa.reserve(n+2); eg.resize(n+2); lca_dep.resize(n+2);
lca_fa.resize(n+2); lca_ma.resize(n+2);
for(int i=0; i<=n; i++) lca_fa[i].resize(22), lca_ma[i].resize(22), dsu_fa[i] = i;
}
int dsu_find(int x) { return dsu_fa[x]==x?x:dsu_fa[x]=dsu_find(dsu_fa[x]); }
void dsu_merge(int x, int y) { dsu_fa[x] = y; }
void add(int u, int v, int c) { eg[u].push_back({v, c}); eg[v].push_back({u, c}); }
void kruskal() {
sort(e.begin(), e.end(), cmp);
for(int i=1; i<=m; i++) {
int x = dsu_find(e[i].u), y = dsu_find(e[i].v);
if(x!=y) dsu_merge(x, y), add(e[i].u, e[i].v, e[i].c);
}
}
void lca_dfs(int u) {
for(int i=1; i<=18; i++) {
lca_fa[u][i] = lca_fa[lca_fa[u][i-1]][i-1];
lca_ma[u][i] = max(lca_ma[u][i-1], lca_ma[lca_fa[u][i-1]][i-1]);
}
for(auto [v, c]:eg[u]) { // c++14
if(v != lca_fa[u][0]) lca_fa[v][0] = u, lca_ma[v][0] = c, lca_dep[v] = lca_dep[u] + 1, lca_dfs(v);
}
}
int lca_query(int u, int v) {
int maxn = 0;
if(lca_dep[u] < lca_dep[v]) swap(u, v);
for(int i=18; i>=0; i--) {
if(lca_dep[lca_fa[u][i]] >= lca_dep[v]) maxn = max(maxn, lca_ma[u][i]), u = lca_fa[u][i];
}
if(u == v) return maxn;
for(int i=18; i>=0; i--) {
if(lca_fa[u][i] != lca_fa[v][i]) {
maxn = max({maxn, lca_ma[u][i], lca_ma[v][i]});
u = lca_fa[u][i];
v = lca_fa[v][i];
}
}
maxn = max({maxn, lca_ma[u][0], lca_ma[v][0]});
return maxn;
}
} g;
void solve()
{
cin >> g.n >> g.m;
g.init();
for(int i=1; i<=g.m; i++) cin >> g.e[i].u >> g.e[i].v >> g.e[i].c;
g.kruskal(); g.lca_dep[1] = 1; g.lca_dfs(1);
vector<ll> sum(g.n+2, 0);
for(int i=2; i<=g.n; i++) sum[i] = sum[i-1] + g.lca_query(i-1, i);
int q, l, r;
cin >> q;
auto query = [&](int l, int r) { return sum[r] - sum[l]; };
while(q--) {
cin >> l >> r;
cout << query(l, r) << '\n';
}
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while(T--) {
solve();
}
return 0;
}
标程2:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
#define ll long long
int n, m;
struct Edge {
int x, y, w;
bool operator<(const Edge& b) {
return w < b.w;
}
};
Edge e[N];
vector<pair<int, int>> E[N];
int Fa[21][N], W[21][N], Dep[N];
ll s[N];
int fa[N];
int find(int x) {
if (x == fa[x])return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y) {
fa[find(x)] = find(y);
}
void dfs(int u, int fa) {
Dep[u] = Dep[fa] + 1;
for (auto& p : E[u]) {
int v = p.first, w = p.second;
if (v == fa)continue;
Fa[0][v] = u; W[0][v] = w;
for (int i = 1; i < 21; ++i) {
Fa[i][v] = Fa[i - 1][Fa[i - 1][v]];
W[i][v] = max(W[i - 1][v], W[i - 1][Fa[i - 1][v]]);
}
dfs(v, u);
}
}
int cal(int u, int v) {
int mx = 0;
if (Dep[u] < Dep[v])swap(u, v);
for (int i = 20; i >= 0; --i) {
if (Dep[Fa[i][u]] >= Dep[v])mx = max(mx, W[i][u]), u = Fa[i][u];
}
if (u == v)return mx;
for (int i = 20; i >= 0; --i) {
if (Fa[i][u] != Fa[i][v]) {
mx = max(mx, W[i][u]);
mx = max(mx, W[i][v]);
u = Fa[i][u], v = Fa[i][v];
}
}
mx = max(mx, W[0][u]);
mx = max(mx, W[0][v]);
return mx;
}
void work() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)fa[i] = i;
for (int i = 1; i <= m; ++i) {
int x, y, w; cin >> x >> y >> w;
e[i] = { x,y,w };
}
sort(e + 1, e + 1 + m);
for (int i = 1; i <= m; ++i) {
if (find(e[i].x) != find(e[i].y)) {
E[e[i].x].push_back({ e[i].y,e[i].w });
E[e[i].y].push_back({ e[i].x,e[i].w });
merge(e[i].x, e[i].y);
}
}
dfs(1, 0);
for (int i = 1; i < n; ++i) {
s[i] = cal(i, i + 1);
s[i] += s[i - 1];
}
int q; cin >> q;
while (q--) {
int l, r; cin >> l >> r;
if (l > r)swap(l, r);
r--;
cout << s[r] - s[l - 1] << '\n';
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T; T = 1;
while (T--) {
work();
}
}
E 灰之魔女裁魔法树
按题意模拟即可,详见代码。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int d[11], n, m, k, h[N];
void update() {
for (int i = k; i >= 1; --i) {
if (d[i])h[d[i]]++;
d[i] = d[i - 1];
}
}
void add(int x) {
d[1] = x;
for (int i = 2; i <= k; ++i) {
if (d[i] == x)d[i] = 0;
}
}
void del(int x) {
for (int i = 1; i <= k; ++i)if (d[i] == x)d[i] = 0;
}
void work() {
cin >> n >> m >> k;
for (int i = 1; i <= m; ++i) {
int op, x; cin >> op >> x;
if (op == 1) {
add(x);
}
else if (op == 2) {
del(x);
}
else {
cout << h[x] + i - 1 << '\n';
}
update();
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
while (T--) {
work();
}
}
F GZHUACM
按要求输出即可,\
注意转义,可以利用c++11的原生字符串。
时间复杂度 。
标程:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve()
{
cout << R"( ,----, ,--, ____
,----.. .' .`| ,--.'| ,---, ,----.. ,' , `.
/ / \ .' .' ; ,--, | : ,--, ' .' \ / / \ ,-+-,.' _ |
| : : ,---, ' .',---.'| : ' ,'_ /| / ; '. | : : ,-+-. ; , ||
. | ;. / | : ./ | | : _' | .--. | | : : : \ . | ;. / ,--.'|' | ;|
. ; /--` ; | .' / : : |.' |,'_ /| : . | : | /\ \ . ; /--` | | ,', | ':
; | ; __ `---' / ; | ' ' ; :| ' | | . . | : ' ;. : ; | ; | | / | | ||
| : |.' .' / ; / ' | .'. || | ' | | | | | ;/ \ \| : | ' | : | : |,
. | '_.' : ; / /--, | | : | ': | | : ' ; ' : | \ \ ,'. | '___ ; . | ; |--'
' ; : \ | / / / .`| ' : | : ;| ; ' | | ' | | ' '--' ' ; : .'|| : | | ,
' | '/ .'./__; : | | ' ,/ : | : ; ; | | : : ' | '/ :| : ' |/
| : / | : .' ; : ;--' ' : `--' \| | ,' | : / ; | |`-'
\ \ .' ; | .' | ,/ : , .-./`--'' \ \ .' | ;/
`---` `---' '---' `--`----' `---` '---')";
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while(T--) {
solve();
}
return 0;
}
G Clannad
题意要在 这 个整数里不重复地选 个整数,使得存在两个的差的绝对值为 的倍数。
假如我们把选出来的 个整数对 取模,会得到 个余数,那么根据鸽雀原理,必然存在两个余数相同,余数相同,说明它们的差恰好为 的倍数。
于是问题转换成,从 个整数里不重复地取 个整数出来,即 ,预处理组合数,特判 即可。
时间复杂度:预处理 ,单次询问 ,总的复杂度 。
标程:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5050;
const int mod = 1e9+7;
ll C[N+2][N+2];
void Init()
{
for(int i=0; i<=N; i++) {
C[i][0] = 1;
for(int j=1; j<=i; j++) {
C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mod;
}
}
}
ll Cal(int n, int m) { return C[m][n]; }
void solve()
{
int n, x;
cin >> n >> x;
n++;
if(x < n) return cout << "-1\n", void();
else return cout << Cal(n, x) << "\n", void();
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
Init();
while(T--) {
solve();
}
return 0;
}
H 性格差劲的久美子
由于出题人水平不足以及疏忽,导致H题数据有误以及题面描述不清晰,对大家造成不好的体验,我们深感歉意。数据已经修改,如果对题目有什么问题,欢迎大家在同步赛评论区指正。
题意:从A中选择一个子序列,且该子序列与B的lcs不超过1,求A中选择的子序列的最长长度。
首先若有某个元素在中不存在,一定可取,设这样的元素个数为。 再考虑其余的元素,定义表示在中出现的区间,对于中的某个序列,假设之前已取了,能接着取需满足,答案就是最长不增子序列长度+cnt
标程:
#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int N = 2e5 + 10;
int s[N], t[N], n, m;
int Tr[N];
void add(int x, int v) {
while (x) {
Tr[x] = max(Tr[x], v);
x -= lowbit(x);
}
}
int ask(int x) {
int ans = 0;
while (x < N) {
ans = max(Tr[x], ans);
x += lowbit(x);
}
return ans;
}
void work() {
cin >> n;
for (int i = 1; i <= n; ++i)cin >> s[i];
cin >> m;
for (int i = 1; i <= m; ++i)cin >> t[i];
map<int, pair<int, int>> p;
for (int i = 1; i <= m; ++i) {
if (p[t[i]].first == 0)p[t[i]] = { i,i };
else p[t[i]].second = max(p[t[i]].second, i);
}
int ans = 0, tot = 0;
for (int i = 1; i <= n; ++i) {
if (p.find(s[i]) == p.end())tot++;
else {
int res = ask(p[s[i]].second) + 1;
ans = max(ans, res);
add(p[s[i]].first, res);
}
}
cout << ans + tot << '\n';
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
work();
}
I Min And Max
答案最小值为从大到小逆序。相邻,。
下面计算最大值: 计算最大浪费空间和,相邻的对有贡献,最大贡献为,且正值和负值数量相同,每个数只能被加一次和减一次,所以最大值为个数里面找到个数+,个数-,因为超过的正负值相互抵消。
所以最大值s+,构造方法大致为。
标程:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int N = 1e5 + 10;
ll v[N];
void work() {
int n; cin >> n;
for (int i = 1; i <= n; ++i) {
int x; cin >> x;
v[i] = (1 << x);
}
sort(v + 1, v + 1 + n);
ll ans = 0, s = 0;
for (int i = 1; i <= n; ++i) {
if (i > (n + 1) / 2)ans += v[i] * 2;
s += v[i];
}
if (n & 1)ans += v[(n + 1) / 2];
cout << s << ' ' << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
work();
}
J 原舟二象性
fft即可,详见代码
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
#define inf std::numeric_limits<ll>::max()
#define pb push_back
using namespace __gnu_pbds;
using namespace std;
const ld PI = acos(-1.0);
#define Please return
#define AC 0
struct _IO
{
_IO()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
} _io;
const ll mod=998244353;
ll qpow(ll n,ll k){
ll ans=1;
while(k){
if(k&1){
ans*=n;
ans%=mod;
}
n*=n;
n%=mod;
k>>=1;
}
return ans;
}
const double eps=1e-6;
const ll maxn=4e5+7;
complex<double> a[maxn];
ll n,m;
ll l,r[maxn];
ll limit=1;
void fft(complex<double> *A,ll type)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(A[i],A[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
complex<double> Wn( cos(PI/mid) , type*sin(PI/mid) );
for(int R=mid<<1,j=0;j<limit;j+=R)
{
complex<double> w(1,0);
for(int k=0;k<mid;k++,w=w*Wn)
{
complex<double> x=A[j+k],y=w*A[j+mid+k];
A[j+k]=x+y;
A[j+mid+k]=x-y;
}
}
}
}
int main()
{
cin>>n;
n--;
for(int i=0;i<=n;i++){
cin>>a[i];
}
while(limit<=n*2) limit<<=1,l++;
for(int i=0;i<limit;i++)
r[i]= ( r[i>>1]>>1 )| ( (i&1)<<(l-1) ) ;
fft(a,1);
for(int i=0;i<=limit;i++) a[i]=a[i]*a[i];
fft(a,-1);
int ans=1;
for(int i=0;i<=n*2;i++){
if((real(a[i])/limit)>eps){
ans*=1;
}
else if((real(a[i])/limit)<-eps){
ans*=-1;
}
else{
ans*=0;
}
}
if(ans==1){
cout<<"woc yuan"<<endl;
}
else if(ans==-1){
cout<<"woc zhou"<<endl;
}
else{
cout<<"woc nong"<<endl;
}
}
K 因子模仿 - easy version
容易发现,答案只取决于 ,为 则茉美香胜利,为 则对手胜利。
问题转化成,单点询问,和维护区间 异或,这里列两种解法。
解法1
维护区间 个数,若区间长度 ,区间 个数为 ,那么异或时相当于把 修改为 ,以线段树为例。
标程:
#include <bits/stdc++.h>
using namespace std;
// Template: std::cout argument list
template<typename T>
void f_o(const T& arg) { std::cout << arg << '\n'; }
template<typename T, typename... Ty_>
void f_o(const T& f_arg, const Ty_&... args) { std::cout << f_arg << ' '; f_o(args...); }
typedef long long ll;
// Modular addition and multiplication
const ll mod = 1e9+7;
ll add(ll &x, ll y) { return x = (x + y + mod) % mod; }
ll mul(ll x, ll y) { return (x * y) % mod; }
// Max Size
const int N = 1e6+5;
// SegmentTree Node
struct Node {
int l, r;
int cnt;
bool lazy;
};
// SegmentTree with Lazytag
struct Segment_lazytree {
#define ls (rt<<1)
#define rs (rt<<1)|1
#define lson ls,l,mid
#define rson rs,mid+1,r
Node t[N<<2];
bool s[N];
void flip(int rt) { t[rt].cnt = t[rt].r-t[rt].l+1 - t[rt].cnt; }
void push_up(int rt) { t[rt].cnt = t[ls].cnt + t[rs].cnt; }
void push_down(int rt) {
if(t[rt].lazy) {
flip(ls); t[ls].lazy ^= 1;
flip(rs); t[rs].lazy ^= 1;
t[rt].lazy = 0;
}
}
void build(int rt, int l, int r) {
t[rt].l = l, t[rt].r = r, t[rt].lazy = 0;
if(l==r) return t[rt].cnt = s[l], void();
int mid = (l+r)>>1;
build(lson);
build(rson);
push_up(rt);
}
void update(int rt, int ql, int qr) {
int l = t[rt].l, r = t[rt].r, len = (r-l)+1;
if(ql<=l && r<=qr) {
flip(rt);
t[rt].lazy ^= 1;
return ;
}
push_down(rt);
int mid = (l+r)>>1;
if(ql<=mid) update(ls, ql, qr);
if(qr>mid) update(rs, ql, qr);
push_up(rt);
}
int query(int rt, int k) {
int l = t[rt].l, r = t[rt].r;
if(l==r && l==k) return t[rt].cnt;
push_down(rt);
int mid = (l+r)>>1;
if(k>mid) return query(rs, k);
else return query(ls, k);
}
} seg;
// Solution
void solve()
{
ll n, q, a1, a2, b1, b2;
cin >> n >> q >> a1 >> a2 >> b1 >> b2;
for(int i=1; i<=n; i++) {
char ch; cin >> ch;
seg.s[i] = (ch=='1');
}
seg.build(1, 1, n);
// query([ql, qr])
auto query = [&](int ql, int qr) {
f_o((seg.query(1, qr))? "Magical Splash Flare" : "HoloPsychon");
};
while(q--) {
int op, ql, qr;
cin >> op >> ql >> qr;
if(op==1) { // op==1:flip([ql, qr])
seg.update(1, ql, qr);
} else { // op==2:query([ql, qr])
query(ql, qr);
}
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
clock_t time1 = clock();
#endif
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// cin >> T;
while(T--) {
solve();
}
return 0;
}
解法2
维护位置异或次数,因为只有 和 ,异或偶数次相当于不变,只要维护 异或次数判断奇偶即可,以树状数组为例。
标程:
#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int N = 1e6 + 10;
int Tr[N], n, m, a1, b1, a2, b2;
void add(int x, int v) {
while (x < N) {
Tr[x] += v;
x += lowbit(x);
}
}
int ask(int x) {
int res = 0;
while (x) {
res += Tr[x];
x -= lowbit(x);
}
return res;
}
void work() {
cin >> n >> m;
cin >> a1 >> b1 >> a2 >> b2;
for (int i = 1; i <= n; ++i) {
char ch; cin >> ch;
if (ch == '1')add(i, 1), add(i + 1, -1);
}
while (m--) {
int op; cin >> op;
if (op == 1) {
int l, r; cin >> l >> r;
add(l, 1); add(r + 1, -1);
}
else {
int l, r; cin >> l >> r;
int res = ask(r) & 1;
if (res) {
cout << "Magical Splash Flare\n";
}
else cout << "HoloPsychon\n";
}
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T; T = 1;
while (T--) {
work();
}
}
L 因子模仿 - hard version
题意求 ,想办法求出这四个值。
发现两个特殊的矩阵:
Mat1:[1,0]
[1,1]
和:
Mat0:[1,1]
[0,1]
维护矩阵:
Matx:[a1,a2]
[b1,b2]
左乘 得到:
[a1+a2,a2]
[b1+b2,b2]
左乘 得到:
[a1,a1+a2]
[b1,b1+b2]
容易发现左乘 就是 的情况,左乘 就是 的情况,由于矩阵相乘具有结合律,那么询问相当于 左乘若干个 或 ,用线段树维护后面部分区间矩阵的乘积即可。
翻转操作,可以维护两种矩阵然后交换,也可以直接转置:
[a1,a2] -> [b2,b1]
[b1,b2] [a2,a1]
转置正确性证明:
[a1,a2] * [1,0] = [a1+a2,a2]
[b1,b2] [1,1] [b1+b2,b2]
[b2,b1] * [1,1] = [b2,b1+b2]
[a2,a1] [0,1] [a2,a1+a2]
标程1:
#include <bits/stdc++.h>
using namespace std;
// Template: std::cout argument list
template<typename T>
void f_o(const T& arg) { std::cout << arg << '\n'; }
template<typename T, typename... Ty_>
void f_o(const T& f_arg, const Ty_&... args) { std::cout << f_arg << ' '; f_o(args...); }
typedef long long ll;
// Modular addition and multiplication and subtraction
const ll mod = 1e9+7;
ll add(ll &x, ll y) { return x = (x + y + mod) % mod; }
ll mul(ll x, ll y) { return (x * y) % mod; }
ll sub(ll x, ll y) { return (x - y + mod) % mod; }
// Max Size
const int N = 1e6+5;
// Const Matrix
ll _A[2][2] = {1, 0, 1, 1}; // Mat1
ll _B[2][2] = {1, 1, 0, 1}; // Mat0
ll _Z[2][2] = {0, 0, 0, 0}; // MatZero
// Matrix
struct Mat {
ll m[2][2];
Mat() { for(int i=0; i<2; i++) for(int j=0; j<2; j++) this->m[i][j] = 0; }
Mat(ll x[2][2]) { for(int i=0; i<2; i++) for(int j=0; j<2; j++) this->m[i][j] = x[i][j]; }
friend Mat operator*(Mat a, Mat b) {
Mat res(_Z);
for(int i=0; i<2; i++) for(int j=0; j<2; j++) for(int k=0; k<2; k++) add(res.m[i][j], mul(a.m[i][k], b.m[k][j]));
return res;
}
void copy(const Mat &b) { for(int i=0; i<2; i++) for(int j=0; j<2; j++) this->m[i][j] = b.m[i][j]; }
} A(_A), B(_B);
// SegmentTree Node
struct Node {
Mat mat[2];
bool lazy;
};
// SegmentTree with Lazytag
struct Segment_lazytree {
#define ls (rt<<1)
#define rs (rt<<1)|1
#define lson ls,l,mid
#define rson rs,mid+1,r
Node t[N<<2];
bool s[N];
void flip(Node& res) {
swap(res.mat[0], res.mat[1]);
}
void push_up(int rt) {
t[rt].mat[0] = t[ls].mat[0] * t[rs].mat[0];
t[rt].mat[1] = t[ls].mat[1] * t[rs].mat[1];
}
void push_down(int rt) {
if(t[rt].lazy) {
flip(t[ls]); t[ls].lazy ^= 1;
flip(t[rs]); t[rs].lazy ^= 1;
t[rt].lazy = 0;
}
}
void build(int rt, int l, int r) {
t[rt].lazy = 0;
if(l == r) {
if(s[l]) t[rt].mat[0].copy(A), t[rt].mat[1].copy(B);
else t[rt].mat[0].copy(B), t[rt].mat[1].copy(A);
return ;
}
int mid = (l+r)>>1;
build(lson);
build(rson);
push_up(rt);
}
void update(int rt, int l, int r, int ql, int qr) {
if(ql<=l && r<=qr) {
flip(t[rt]);
t[rt].lazy ^= 1;
return ;
}
push_down(rt);
int mid = (l+r)>>1;
if(ql<=mid) update(lson, ql, qr);
if(qr>mid) update(rson, ql, qr);
push_up(rt);
}
Mat query(int rt, int l, int r, int ql, int qr) {
if(ql<=l && r<=qr) return t[rt].mat[0];
push_down(rt);
int mid = (l+r)>>1;
if(ql > mid) return query(rson, ql, qr);
else if(qr <= mid) return query(lson, ql, qr);
else return query(lson, ql, qr)*query(rson, ql, qr);
}
} seg;
/*
Matx:[a,b]
[c,d]
Mat1:[1,0]
[1,1]
Mat0:[1,1]
[0,1]
Matx * Sum(Mat01-Mat10)
-> MatAns:[f(a,b),g(a,b)]
[f(c,d),g(c,d)]
*/
// Solution
void solve()
{
ll n, q, a1, a2, b1, b2;
cin >> n >> q >> a1 >> a2 >> b1 >> b2;
ll x[2][2] = {a1, a2, b1, b2};
Mat y(x);
for(int i=1; i<=n; i++) {
char ch; cin >> ch;
seg.s[i] = (ch=='1');
}
seg.build(1, 1, n);
// query([ql, qr])
auto query = [&](int ql, int qr) {
Mat sum = y * seg.query(1, 1, n, ql, qr);
ll A1 = sum.m[0][0], A2 = sum.m[1][0], B1 = sum.m[0][1], B2 = sum.m[1][1];
ll ans = sub(mul(A1, A2), mul(B1, B2));
cout << ans << '\n';
};
while(q--) {
int op, ql, qr;
cin >> op >> ql >> qr;
if(op==1) { // op==1:flip([ql, qr])
seg.update(1, 1, n, ql, qr);
} else { // op==2:query([ql, qr])
query(ql, qr);
}
}
}
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// cin >> T;
while(T--) {
solve();
}
return 0;
}
标程2:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 4e6 + 10;
const int mod = 1e9 + 7;
struct mat {
ll m[2][2];
mat() {
memset(m, 0, sizeof m);
}
mat operator*(const mat& b) {
mat res;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < 2; ++k) {
res.m[i][j] += m[i][k] * b.m[k][j];
res.m[i][j] %= mod;
}
}
}
return res;
}
void rev() {
mat tmp = *this;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
m[i][j] = tmp.m[!i][!j];
}
}
}
};
int n, q, a1, a2, b1, b2;
mat Tr[N], one, A, B;
bool rev[N];
string s;
void pushup(int rt) {
Tr[rt] = Tr[rt << 1] * Tr[rt << 1 | 1];
}
void pushdown(int rt) {
if (rev[rt]) {
Tr[rt << 1].rev(); Tr[rt << 1 | 1].rev();
rev[rt << 1] ^= 1; rev[rt << 1 | 1] ^= 1; rev[rt] ^= 1;
}
}
void update(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
Tr[rt].rev();
rev[rt] ^= 1;
return;
}
pushdown(rt);
int mid = l + r >> 1;
if (ql <= mid)update(rt << 1, l, mid, ql, qr);
if (qr > mid)update(rt << 1 | 1, mid + 1, r, ql, qr);
pushup(rt);
}
mat ask(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return Tr[rt];
}
pushdown(rt);
int mid = l + r >> 1;
mat res = one;
if (ql <= mid)res = res * ask(rt << 1, l, mid, ql, qr);
if (qr > mid)res = res * ask(rt << 1 | 1, mid + 1, r, ql, qr);
return res;
}
void build(int rt, int l, int r) {
if (l == r) {
if (s[l - 1] == '1')Tr[rt] = A;
else Tr[rt] = B;
return;
}
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void work() {
cin >> n >> q;
cin >> a1 >> a2 >> b1 >> b2;
one.m[0][0] = one.m[1][1] = 1;
A = B = one;
A.m[1][0] = B.m[0][1] = 1;
mat init;
init.m[0][0] = a1, init.m[0][1] = a2;
init.m[1][0] = b1, init.m[1][1] = b2;
cin >> s;
build(1, 1, n);
while (q--) {
int op, l, r; cin >> op >> l >> r;
if (op == 1) {
update(1, 1, n, l, r);
}
else {
mat res = ask(1, 1, n, l, r);
res = init * res;
cout << (res.m[0][0] * res.m[1][0] % mod - res.m[0][1] * res.m[1][1] % mod + mod) % mod << '\n';
}
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T; T = 1;
while (T--) {
work();
}
}