2019 CCPC wannfly winter camp Day 8
E - Souls-like Game
直接线段树合并矩阵会被卡T掉,因为修改的复杂度比询问的复杂度多一个log,所以我们考虑优化修改。
修改的瓶颈在于打lazy的时候, 所以我们预处理出每个修改矩阵2的幂次,然后直接更新。
//#pragma GCC optimize(2) //#pragma GCC optimize(3) //#pragma GCC optimize(4) //#pragma GCC optimize("unroll-loops") //#pragma comment(linker, "/stack:200000000") //#pragma GCC optimize("Ofast,no-stack-protector") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native") #include<bits/stdc++.h> #define fi first #define se second #define db double #define mp make_pair #define pb push_back #define pi acos(-1.0) #define ll long long #define vi vector<int> #define mod 998244353 #define ld long double //#define C 0.5772156649 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 #define pll pair<ll,ll> #define pil pair<int,ll> #define pli pair<ll,int> #define pii pair<int,int> #define ull unsigned long long //#define base 1000000000000000000 #define fin freopen("a.txt","r",stdin) #define fout freopen("a.txt","w",stdout) #define fio ios::sync_with_stdio(false);cin.tie(0) inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} inline void sub(ll &a,ll b){a-=b;if(a<0)a+=mod;} inline void add(ll &a,ll b){a+=b;if(a>=mod)a-=mod;} template<typename T>inline T const& MAX(T const &a,T const &b){return a>b?a:b;} template<typename T>inline T const& MIN(T const &a,T const &b){return a<b?a:b;} inline ll qp(ll a,ll b){ll ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;} inline ll qp(ll a,ll b,ll c){ll ans=1;while(b){if(b&1)ans=ans*a%c;a=a*a%c,b>>=1;}return ans;} using namespace std; const ull ba=233; const db eps=1e-9; const ll INF=0x3f3f3f3f3f3f3f3f; const int N = 200000+10,maxn=4000000+10,inf=0x3f3f3f3f; int nn, tot; struct Matrix { int a[3][3]; Matrix() { memset(a, 0, sizeof(a)); } void read() { for(int i=0;i<3;i++)for(int j=0;j<3;j++)scanf("%d",&a[i][j]); } void pr() { for(int i=0;i<3;i++) { for(int j=0;j<3;j++)printf("%d ",a[i][j]); puts(""); } } void init() { for(int i = 0; i < 3; i++) a[i][i] = 1; } Matrix operator * (const Matrix &B) const { Matrix C; for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) for(int k = 0; k < 3; k++) C.a[i][j] = (C.a[i][j] + 1ll * a[i][k] * B.a[k][j]) % mod; return C; } Matrix operator ^ (int b) { Matrix C; C.init(); Matrix A = (*this); while(b) { if(b & 1) C = C * A; A = A * A; b >>= 1; } return C; } } o[N<<2], tmp[N][20]; int lazy[N<<2], depth[N<<2], cnt; bool ok[N<<2]; void pushup(int rt){o[rt]=o[rt<<1]*o[rt<<1|1];} void pushdown(int l,int r,int rt) { if(!ok[rt]) return; int m=(l+r)>>1; o[rt<<1]=tmp[lazy[rt]][depth[rt<<1]]; o[rt<<1|1]=tmp[lazy[rt]][depth[rt<<1|1]]; lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt]; ok[rt<<1] = ok[rt<<1|1] = true; ok[rt] = false; } void build(int l,int r,int rt,int deep) { depth[rt] = deep; ok[rt] = false; if(l==r){if(l <= nn) o[rt].read();return;} int m=(l+r)>>1; build(ls, deep-1);build(rs, deep-1); pushup(rt); } void update(int L,int R,int who,int l,int r,int rt) { if(L<=l&&r<=R) { o[rt] = tmp[who][depth[rt]]; lazy[rt] = who; ok[rt] = true; return; } pushdown(l,r,rt); int m=(l+r)>>1; if(L<=m)update(L,R,who,ls); if(m<R)update(L,R,who,rs); pushup(rt); } Matrix query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) { return o[rt]; } pushdown(l,r,rt); int m=(l+r)>>1; Matrix te;te.init(); if(L<=m)te=te*query(L,R,ls); if(m<R)te=te*query(L,R,rs); return te; } int main() { int n, m, p; scanf("%d%d",&n,&m);n--; nn = n; for(p = 0; (1 << p) < n; p++); n = 1 << p; build(1, n, 1, p); while(m--) { int op,l,r;scanf("%d%d%d",&op,&l,&r); if(op==1) { cnt++; tmp[cnt][0].read(); for(int i = 1; i < 20; i++) tmp[cnt][i] = tmp[cnt][i - 1] * tmp[cnt][i - 1]; update(l,r,cnt,1,n,1); } else { --r; Matrix te=query(l,r,1,n,1); ll ans=0; for(int i=0;i<3;i++)for(int j=0;j<3;j++)add(ans,1ll*te.a[i][j]); printf("%lld\n",ans); } } return 0; } /* */
G - 穗乃果的考试
我们考虑 求和 i * f[ i ] 实际上就是求每个点被多少矩形包括。
那么 求和 i * i * f[ i ] 就是求任意两个点对被多少矩形包括之和, 这个我们可以用树状数组维护。
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PLL pair<LL, LL> #define PLI pair<LL, int> #define PII pair<int, int> #define SZ(x) ((int)x.size()) #define ull unsigned long long using namespace std; const int N = 2000 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; struct Bit { LL a[N]; void modify(int x, int val) { for(int i = x; i < N; i += i & -i) a[i] += val; } LL sum(int x) { LL ans = 0; for(int i = x; i; i -= i & -i) ans += a[i]; return ans; } LL query(int L, int R) { if(L > R) return 0; return sum(R) - sum(L - 1); } } bit[2]; int n, m; char s[N][N]; int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%s", s[i] + 1); LL ans = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { pre = ans; if(s[i][j] == '1') { ans = (ans + 2 * bit[0].sum(j) * (n-i+1) % mod * (m-j+1) % mod); ans = (ans + 2 * bit[1].query(j + 1, m) * j % mod * (n-i+1) % mod); bit[0].modify(j, i * j); bit[1].modify(j, i * (m-j+1)); ans = (ans + 1ll * i * j * (n-i+1) % mod * (m-j+1) % mod) % mod; } } } printf("%lld\n", ans); return 0; } /* */
A - Aqours
我们可以发现一颗子树返回上来的最优节点一定是编号最小的节点。
这题卡dfs, 我按叶子节点的编号顺序把边排序,然后用栈模拟dfs。
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PLL pair<LL, LL> #define PLI pair<LL, int> #define PII pair<int, int> #define SZ(x) ((int)x.size()) #define ull unsigned long long using namespace std; const int N = 3e6 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; int n, cnt, tot, p[N], mn[N], mnlen[N], bottom[N]; int ans[N]; vector<int> G[N]; vector<int> leaf; bool cmp(const int& a, const int& b) { return mn[a] < mn[b]; } struct node { int who, len, p; }; inline int read() { char ch = getchar(); int x = 0, f = 1; while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); } while('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int main() { n = read(); if(n == 1) { puts("1 -1"); return 0; } for(int i = 2; i <= n; i++) { p[i] = read(); G[p[i]].push_back(i); } for(int i = 1; i <= n; i++) mn[i] = inf, mnlen[i] = inf, bottom[i] = inf; for(int i = n; i > 1; i--) { if(!SZ(G[i])) mn[i] = i, bottom[i] = 0, leaf.push_back(i); mn[p[i]] = min(mn[p[i]], mn[i]); } reverse(leaf.begin(), leaf.end()); for(int i = 1; i <= n; i++) sort(G[i].begin(), G[i].end(), cmp); stack<PII> stk; stk.push({1, 0}); while(SZ(stk)) { PII u = stk.top(); stk.pop(); if(!u.se) { ans[u.fi] = mnlen[u.fi]; } if(u.se >= SZ(G[u.fi])) { bottom[p[u.fi]] = min(bottom[p[u.fi]], bottom[u.fi] + 1); } else { mnlen[u.fi] = min(mnlen[u.fi], bottom[u.fi]); stk.push(mk(u.fi, u.se + 1)); mnlen[G[u.fi][u.se]] = mnlen[u.fi] + 1; stk.push(mk(G[u.fi][u.se], 0)); } } for(auto& u : leaf) { int val = ans[u]; printf("%d %d\n", u, val >= inf ? -1 : val); } return 0; } /* */