过了4题,铜牌qwq
补题花了挺长时间
A Array
比赛的时候写了个加标记,乘标记,次方标记,麻烦死,最后写到比赛结束也没写完QAQ
太难顶了
比赛结束后才把这看成是一个映射
这题也反应出了我对懒标记的不熟悉
补这题前复习了线段树,并大改了自己的线段树模板。
补完这题后,又计时默写了几次线段树模板,现在能15min内默写出线段树加标记+乘标记的模板了
思路:关键的一点就是注意到模数非常小,我们记录每个节点上有多少个数取膜后为某一值,用一个数组来存这个个数
比如用zz[x]表示这个区间上取膜后为x的数有zz[x]个
区间加、乘、次方实际上都是对这个数组进行了映射操作
所以我们就用一个结构体为映射的懒标记
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; const int MAXN = 1e5 + 7; int a[MAXN]; int n, p, q; int k; int qpow(int a, int b) { int res = 1; for (; b; a = a * a % p) { if (b & 1) res = res * a % p; b >>= 1; } return res; } struct F { int pt[30]; }; struct NODE { int l, r, mid, len; bool ff;//懒标记 int zz[30]; F f;//懒标记 NODE* lson; NODE* rson; }; NODE* head = new(NODE); void upd(NODE* pp) { for (int i = 0; i < p; i++) { pp->zz[i] = pp->lson->zz[i] + pp->rson->zz[i]; } } void build(NODE* pp, int l, int r) { pp->l = l; pp->r = r; pp->mid = l + r >> 1; pp->len = r - l + 1; pp->lson = new(NODE); pp->rson = new(NODE); for (int i = 0; i < p; i++) pp->f.pt[i] = i; pp->ff = false; if (l == r) { for (int i = 0; i < p; i++) pp->zz[i] = 0; int x; scanf("%d", &x); x %= p; pp->zz[x]++; return; } int mid = pp->mid; build(pp->lson, l, mid); build(pp->rson, mid + 1, r); upd(pp); } void pd(NODE* pp) { if (pp->l == pp->r) return; pp->lson->ff = true; pp->rson->ff = true; int tmp[30] = { 0 }; for (int i = 0; i < p; i++) { pp->lson->f.pt[i] = pp->f.pt[pp->lson->f.pt[i]]; pp->rson->f.pt[i] = pp->f.pt[pp->rson->f.pt[i]]; } for (int i = 0; i < p; i++) { tmp[pp->f.pt[i]] += pp->lson->zz[i]; } for (int i = 0; i < p; i++) { pp->lson->zz[i] = tmp[i]; tmp[i] = 0; } for (int i = 0; i < p; i++) { tmp[pp->f.pt[i]] += pp->rson->zz[i]; } for (int i = 0; i < p; i++) { pp->rson->zz[i] = tmp[i]; } pp->ff = false; for (int i = 0; i < p; i++) pp->f.pt[i] = i; } void CHANGE(NODE* pp, int l, int r, F f) { if (pp->l == l && pp->r == r) { pp->ff = true; int tmp[30] = { 0 }; for (int i = 0; i < p; i++) { pp->f.pt[i] = f.pt[pp->f.pt[i]]; tmp[f.pt[i]] += pp->zz[i]; } for (int i = 0; i < p; i++) pp->zz[i] = tmp[i]; return; } if(pp->ff) pd(pp); int mid = pp->mid; if (r <= mid) CHANGE(pp->lson, l, r, f); else if (l > mid) CHANGE(pp->rson, l, r, f); else { CHANGE(pp->lson, l, mid, f); CHANGE(pp->rson, mid + 1, r, f); } upd(pp); } int sc[30]; void Q(NODE* pp, int l, int r) { if (pp->l == l && pp->r == r) { for (int i = 0; i < p; i++) { sc[i] += pp->zz[i]; } return; } if (pp->ff) pd(pp); int mid = pp->mid; if (r <= mid)Q(pp->lson, l, r); else if (l > mid) Q(pp->rson, l, r); else { Q(pp->lson, l, mid); Q(pp->rson, mid + 1, r); } } int main() { cin >> n >> p; build(head, 1, n); cin >> q; int op, l, r; for (int e = 1; e <= q; e++) { scanf("%d", &op); if (op == 1) { scanf("%d%d%d", &l, &r, &k); F f; for (int i = 0; i < p; i++) { f.pt[i] = (i + k) % p; } CHANGE(head, l, r, f); } else if (op == 2) { scanf("%d%d%d", &l, &r, &k); F f; k %= p; for (int i = 0; i < p; i++) { f.pt[i] = (i * k) % p; } CHANGE(head, l, r, f); } else if (op == 3) { scanf("%d%d%d", &l, &r, &k); F f; for (int i = 0; i < p; i++) { f.pt[i] = qpow(i, k); } CHANGE(head, l, r, f); } else if (op == 4) { scanf("%d%d%d", &l, &r, &k); for (int i = 0; i < p; i++) sc[i] = 0; Q(head, l, r); int ans = 0, res = 0; for (int i = 0; i < p; i++) { res = qpow(i, k); ans += res * sc[i]; ans %= p; } printf("%d ", ans); } else if (op == 5) { scanf("%d%d%d", &l, &r, &k); for (int i = 0; i < p; i++) sc[i] = 0; Q(head, l, r); int ans = 1, res = 0; for (int i = 0; i < p; i++) { res = qpow(i, sc[i]); ans = ans * res % p; } printf("%d ", ans); } } return 0; }
I Intersections
这题比A题简单,比赛时应该开这题的qwq
就是一个最短路 (趴)
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #include<vector> using namespace std; const int MAXN = 3e5; const long long INF = (long long)1<<60; int n,m,xs,ys,xt,yt; long long a[MAXN],b[MAXN],c[MAXN],w[MAXN],dis[MAXN]; void dijkstra(int st){ priority_queue<pair<long long,int>,vector<pair<long long ,int> >,greater<pair<long long,int> > > que; dis[st] = 0; que.push(make_pair(dis[st],st)); while(!que.empty()){ int node = que.top().second; long long dt = que.top().first; if(dis[node]<dt){ que.pop(); continue; } que.pop(); long long k = dis[node] / (a[node] + b[node]); long long re = dis[node] - k * (a[node] + b[node]); if(re<a[node]){ long long ad = a[node] - re; int po = node - m; if(po > 0 && dis[po] > dis[node] + w[po]){ dis[po] = dis[node] + w[po]; que.push(make_pair(dis[po],po)); } po = node + m; if(po <= n*m && dis[po] > dis[node] + w[node]){ dis[po] = dis[node] + w[node]; que.push(make_pair(dis[po],po)); } if(node % m){ po = node + 1; if(dis[po] > dis[node] + ad + c[node]){ dis[po] = dis[node] + ad + c[node]; que.push(make_pair(dis[po],po)); } } if(node % m != 1){ po = node - 1; if(dis[po] > dis[node] + ad + c[po]){ dis[po] = dis[node] + ad + c[po]; que.push(make_pair(dis[po],po)); } } } else{ int po = node - m; if(po > 0 && dis[po] > (k+1) * (a[node] + b[node]) + w[po] ){ dis[po] = (k+1) * (a[node] + b[node]) + w[po]; que.push(make_pair(dis[po],po)); } po = node + m; if(po <= n*m && dis[po] > (k+1) * (a[node] + b[node]) + w[node]){ dis[po] = (k+1) * (a[node] + b[node]) + w[node]; que.push(make_pair(dis[po],po)); } if(node % m){ po = node + 1; if(dis[po] > dis[node] + c[node]){ dis[po] = dis[node] + c[node]; que.push(make_pair(dis[po],po)); } } if(node % m != 1){ po = node - 1; if(dis[po] > dis[node] + c[po]){ dis[po] = dis[node] + c[po]; que.push(make_pair(dis[po],po)); } } } } } int main() { cin>>n>>m>>xs>>ys>>xt>>yt; int id = 0; for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ id++; scanf("%lld",&a[id]); dis[id] = INF; } } id = 0; for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ id++; scanf("%lld",&b[id]); } } id = 0; for(int i = 1;i <= n;i++){ for(int j = 1;j <= m - 1;j++){ id++; scanf("%lld",&c[id]); } c[++id] = 0; } id = 0; for(int i = 1;i <= n - 1;i++){ for(int j = 1;j <= m;j++){ id++; scanf("%lld",&w[id]); } } for(int j = 1;j <= m;j++) w[++id] = 0; dijkstra((xs - 1) * m + ys); cout<<dis[(xt - 1) * m + yt]<<endl; return 0; }
E Eliminate the Virus
很有意思的一题
思路:n <= 16,要想到用状态来表示,算出每个状态(st1)模拟一遍感染后得到的新状态 (st2),f [st1] = st2,
注:f 是一个单值映射,但 f 的反函数不一定是单值函数,所以不要利用 f 的反函数
题目虽然没说要最小步数,但步数也是有限制的,所以最好求最小步数
这里状态不能从小到大遍历,因为有些状态可能是由比它大的状态转移过来
所以我们就用bfs,从最大状态bfs,求出到各个状态的距离,如果dis[0]!=INF,说明存在答案
接下来的这步就是求答案了
求答案不能慌,再写一段来求答案,不能急于求成
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int INF = 1e6; const int MAXZT = 1 << 16; int f[MAXZT + 10]; int cnt[MAXZT + 10]; int graph[18][18]; int ans[MAXZT + 10]; int dis[MAXZT + 10]; int pre[MAXZT + 10]; bool vis[MAXZT + 10] = {false}; int n, m, k; void init() { bool b[18] = { false }; for (int st = 0; st < 1 << n; st++) { dis[st] = INF; pre[st] = -1; vis[st] = false; for (int i = 0; i < n; i++) b[i] = false; for (int i = 0; i < n; i++) { if (st & (1 << i)) { for (int j = 1; j <= n; j++) { if (graph[i + 1][j]) b[j - 1] = true; } } } int res = 0; for (int i = 0; i < n; i++) { if (b[i]) res += 1 << i; } f[st] = res; int st2 = st; while (st2) { if (st2 & 1) cnt[st] ++; st2 >>= 1; } } } void bfs(int st) { queue<int>que; que.push(st); vis[st] = true; dis[st] = 0; while (!que.empty()) { st = que.front(); que.pop(); for (int st2 = st; ; st2--, st2 &= st) {//遍历子集 if (cnt[st ^ st2] <= k) { if (!vis[f[st2]]) { dis[f[st2]] = dis[st] + 1; pre[f[st2]] = st; vis[f[st2]] = true; que.push(f[st2]); } } if (st2 == 0) break; } } } int main() { cin >> n >> m >> k; int u, v; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) graph[i][j] = 0; for (int i = 1; i <= m; i++) { cin >> u >> v; graph[u][v] = graph[v][u] = 1; } init(); int max_st = (1 << n) - 1; bfs(max_st); if (dis[0] == INF) { cout << -1 << endl; return 0; } cout << dis[0] << endl; for (int tt = 0; tt != max_st;) {//找答案 int pp = pre[tt]; for (int st2 = pp; ; st2--, st2 &= pp) {//遍历子集 if (cnt[pp ^ st2] <= k) { if (f[st2] == tt) { ans[pp] = pp ^ st2; break; } } if (st2 == 0) break; } tt = pp; } int pos = 0; for (int e = 1; e <= dis[0];e++) { pos = pre[pos]; for (int i = 0; i < n; i++) { if (ans[pos] & (1 << i)) { printf("%c", 'a' + i); } } printf(" "); } return 0; }
G Grid Coloring
一个星期才A这题,难顶
这题原来是结论题,难的一匹,看了题解才会这题,原来是有规律的
刚学矩阵加速,就来做这题,然后一直wa,我写了个暴力来对拍,然后发现前面几项对拍都一样,从某项开始就不一样了,
于是我一直在debug我的矩阵,还是不能一样
后来我又写了一个暴力+剪枝的对拍,发现原来是我之前的暴力答案错了。。我一直以为我的矩阵错了,调死调不出来
既然矩阵没错,哪里wa了呢,我又继续对拍,然后发现,原来是n >= 5写成了n == 5了草。。。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> using namespace std; const long long MOD = 1e9+7; const int MAXN = 103; int ST[100]; struct Martix { int n; long long mat[MAXN][MAXN]; void init() { memset(mat, 0, sizeof(mat)); } void E_init() { memset(mat, 0, sizeof(mat)); for (int i = 1; i <= n; i++) mat[i][i] = (long long)1; } void scan() { for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) scanf("%lld", &mat[i][j]); } void print() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) cout << mat[i][j] << " "; cout << endl; } } Martix operator *(Martix b) { Martix tmp; tmp.init(); tmp.n = n; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) { tmp.mat[i][j] += (mat[i][k] * b.mat[k][j]); tmp.mat[i][j] = (MOD + tmp.mat[i][j] % MOD) % MOD; } return tmp; } }; Martix Mat_qpow(Martix A, long long k) { Martix ret; ret.n = A.n; ret.E_init(); for (; k; k >>= 1, A = A * A){ if (k & 1) ret = ret * A; } return ret; } int n,m; bool check(int st,int n){ int a[4],b[4]; for(int i = 1;i <= n ;i++){ if(st&1) b[n-i+1] = 1; else b[n-i+1] = 0; st>>=1; } if(n == 3 && b[1] == b[2] && b[2] == b[3]) return false; for(int i = 1;i <= n;i++){ if(st&1) a[n-i+1] = 1; else a[n-i+1] = 0; st>>=1; } if(n == 3 && a[1] == a[2] && a[2] == a[3]) return false; return true; } bool ok(int st1,int st2,int n){ int a[4],b[4],c[4],d[4]; for(int i = 1;i <= n ;i++){ if(st1&1) b[n-i+1] = 1; else b[n-i+1] = 0; st1>>=1; } if(n == 3 && b[1] == b[2] && b[2] == b[3]) return false; for(int i = 1;i <= n;i++){ if(st1&1) a[n-i+1] = 1; else a[n-i+1] = 0; st1>>=1; } if(n == 3 && a[1] == a[2] && a[2] == a[3]) return false; for(int i = 1;i <= n;i++){ if(st2&1) d[n-i+1] = 1; else d[n-i+1] = 0; st2>>=1; } if(n == 3 && d[1] == d[2] && d[2] == d[3]) return false; for(int i = 1;i <= n;i++){ if(st2&1) c[n-i+1] = 1; else c[n-i+1] = 0; st2>>=1; } if(n == 3 && c[1] == c[2] && c[2] == c[3]) return false; for(int i = 1;i <= n;i++) if(b[i]!=c[i]) return false; for(int i = 1;i <= n;i++){ if(a[i] == b[i] && b[i] == d[i]) return false; } if(n == 3 && a[1] == b[2] && c[2] == d[3]) return false; if(n == 3 && a[3] == b[2] && c[2] == d[1]) return false; return true; } int main() { int T; cin>>T; while(T--){ cin>>n>>m; if(n>m) swap(n,m); if(n==1&&m==1){ cout<<2<<endl; continue; } if(n==4&&m==4){ cout<<18<<endl; continue; } if(n==4) { cout<<14<<endl; continue; } if(n>=5) {//写成n==5了啊啊啊啊啊啊啊啊 cout<<8<<endl; continue; } if(n<=3){ Martix A,B; int tot = 0; for(int st = 0;st < 1<<n*2;st++){ if(check(st,n)) ST[++tot] = st; } A.n = B.n = tot + 1; A.init();B.init(); for(int i = 1;i <= tot;i++){ for(int j = 1;j <= tot;j++){ if(ok(ST[j],ST[i],n)){ A.mat[i][j] = (long long)1; } } } B = Mat_qpow(A,(long long)m-(long long)2); Martix C; C.n = tot + 1; C.init(); for(int i = 1;i<=tot;i++) C.mat[i][1] = (long long)1; B = B * C; long long ans = 0; for(int i = 1;i<=tot;i++) { ans += B.mat[i][1]; ans %= MOD; } cout << ans << endl; } } return 0; }