A - All-one Matrices
题意:
给定一个n × m的 01矩阵,输出极大全1子矩阵的个数。
思路:
先搞出每个点向下能走的距离。
利用单调栈维护每个点最左能到达的值,由于有相同的情况,所以要跑两次单调栈,
第一次求出每个点最左能到的地方。
第二次要弹栈的,判断是否能作为子矩阵的最上层,计入答案。
// #pragma GCC optimize(2) // #pragma GCC optimize(3) // #pragma GCC optimize(4) #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9+7; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } /**********showtime************/ const int maxn = 3009; char str[maxn][maxn]; int sum[maxn][maxn]; int dp[maxn][maxn]; int ls[maxn]; stack<pii>st; int main(){ int n,m; scanf("%d%d", &n, &m); for(int i=1; i<=n; i++) scanf("%s", str[i] + 1); for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { sum[i][j] = sum[i][j-1]; if(str[i][j] == '1') sum[i][j] ++; } } for(int i=n; i>=1; i--) { for(int j=1; j<=m; j++) { if(str[i][j] == '1') dp[i][j] = dp[i+1][j] + 1; else dp[i][j] = 0; } } int ans = 0; for(int i=1; i<=n; i++) { // debug(i); while(!st.empty()) st.pop(); st.push(pii(-1, 0)); for(int j=1; j<=m; j++) { while(!st.empty() && st.top().fi >= dp[i][j]) { st.pop(); } ls[j] = st.top().se + 1; st.push(pii(dp[i][j], j)); } while(!st.empty()) st.pop(); for(int j=1; j<=m + 1; j++) { while(!st.empty() && st.top().fi > dp[i][j]) { int le = ls[st.top().se], ri = j - 1; st.pop(); if(sum[i-1][ri] - sum[i-1][le-1] == ri - le + 1) continue; ans++; } if(j < m + 1 && (st.empty() || dp[i][j] > st.top().fi) )st.push(pii(dp[i][j], j)); } } printf("%d ", ans); return 0; } /* 5 5 11111 11110 11100 11100 10000 3 6 001000 011011 010011 */
D-Distance
思路
考虑非情况,如果点数不多,就枚举点数,如果点数很多,就把这么多点的影响通过bfs记录下来。
/* * @Author: chenkexing * @Date: 2019-08-11 21:48:52 * @Last Modified by: chenkexing * @Last Modified time: 2019-08-11 23:23:57 */ // #pragma GCC optimize(2) // #pragma GCC optimize(3) // #pragma GCC optimize(4) #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<int, pii> p3; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9+7; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } /**********showtime************/ const int maxn = 1e5+9; int n,m,h,q; int getid(int x, int y, int z) { return (z - 1)* (n * m) + (x-1)*m + y; } vector<int>vx,vy,vz; int dis[maxn]; int xia[8][3] = {{0,0,1},{0,1,0},{1,0,0}, {-1, 0 ,0 }, {0, -1 ,0 }, {0, 0 ,-1 }}; void rebuild(){ queue< p3>que; for(int i=0; i<vx.size(); i++) { int nx = vx[i]; int ny = vy[i]; int nz = vz[i]; dis[getid(nx, ny, nz)] = 0; que.push(p3(nx, pii(ny, nz))); } vx.clear(); vy.clear(); vz.clear(); while(!que.empty()) { p3 tmp = que.front(); que.pop(); int x = tmp.fi; int y = tmp.se.fi; int z = tmp.se.se; for(int i=0; i<6; i++) { int nx = x + xia[i][0]; int ny = y + xia[i][1]; int nz = z + xia[i][2]; if(nx <= 0 || nx > n || ny <= 0|| ny > m || nz <= 0 || nz > h) continue; if(dis[getid(nx, ny, nz)] > dis[getid(x, y, z)] + 1) { dis[getid(nx, ny, nz)] = dis[getid(x, y, z)] + 1; que.push(p3(nx, pii(ny, nz))); } } } } int main(){ scanf("%d%d%d%d", &n, &m, &h, &q); int E = sqrt(n * m * h) + 1; memset(dis, inf, sizeof(dis)); while(q--) { int op, x, y, z; scanf("%d%d%d%d", &op, &x, &y, &z); if(op == 1) { vx.pb(x); vy.pb(y); vz.pb(z); } else { int ans = dis[getid(x, y, z)]; for(int i=0; i<vx.size(); i++) { int nx = vx[i]; int ny = vy[i]; int nz = vz[i]; ans = min(ans, abs(nx - x) + abs(ny - y) + abs(nz - z)); } printf("%d ", ans); } if(vx.size() >= E) rebuild(); } return 0; }
E - Explorer
可撤回的并查集
利用线段树优化
注意每次从左子树或者右子树回来的时候,都要进行清空。
// #pragma GCC optimize(2) // #pragma GCC optimize(3) // #pragma GCC optimize(4) #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9+7; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } const int N = 1e5+9; /// 可撤回并查集模板 struct UFS { stack<pair<int*, int>> stk; int fa[N], rnk[N]; inline void init(int n) { for (int i = 0; i <= n; ++i) fa[i] = i, rnk[i] = 0; } inline int Find(int x) { while(x^fa[x]) x = fa[x]; return x; } inline void Merge(int x, int y) { x = Find(x), y = Find(y); if(x == y) return ; if(rnk[x] <= rnk[y]) { stk.push({fa+x, fa[x]}); fa[x] = y; if(rnk[x] == rnk[y]) { stk.push({rnk+y, rnk[y]}); rnk[y]++; } } else { stk.push({fa+y, fa[y]}); fa[y] = x; } } inline void Undo() { *stk.top().fi = stk.top().se; stk.pop(); } }T; /**********showtime************/ const int maxn = 1e5+9; int n,m; struct E{ int u, v, le, ri; void init(int U, int V, int Le, int Ri) { u = U; v = V; le = Le; ri = Ri; } } edge[maxn]; vector<int>vec; int getid(int x) { return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1; } vector <int> node[maxn * 8]; int sz[maxn * 8]; void update(int L, int R, int id, int le, int ri, int rt) { if(le >= L && ri <= R) { node[rt].pb(id); return; } int mid = (le + ri) >> 1; if(mid >= L) update(L, R, id, le, mid, rt<<1); if(mid < R) update(L, R, id, mid+1, ri, rt<<1|1); } ll ans = 0; void dfs(int le, int ri, int rt) { for(int id : node[rt]) { T.Merge(edge[id].u, edge[id].v); } int sz = T.stk.size(); if(le == ri) { if(T.Find(1) == T.Find(n)) { // cout<<ri<<endl; ans += vec[ri] - vec[le-1]; } return; } int mid = (le + ri) >> 1; dfs(le, mid, rt<<1); while(T.stk.size() > sz) { T.Undo(); } dfs(mid+1, ri, rt<<1|1); while(T.stk.size() > sz) { T.Undo(); } } int main(){ scanf("%d%d", &n, &m); T.init(n); for(int i=1; i<=m; i++) { int u,v,le,ri; scanf("%d%d%d%d", &u, &v, &le, &ri); edge[i].init(u, v, le, ri+1); vec.pb(le); vec.pb(ri + 1); } sort(vec.begin(), vec.end()); vec.erase(unique(vec.begin(), vec.end()), vec.end()); int tot = vec.size(); for(int i=1; i<=m; i++) { int l = getid(edge[i].le); int r = getid(edge[i].ri) - 1; // cout<<l<<" " << r << endl; update(l, r, i, 1, tot-1, 1); } ans = 0; dfs(1, tot-1, 1); printf("%lld ", ans); return 0; }
I - Inner World
先把n个子树看成一颗树,相同的点合并到一起,附加一个区间信息就行了。
然后利用dfs序+前缀和的思想处理询问即可
// #pragma GCC optimize(2) // #pragma GCC optimize(3) // #pragma GCC optimize(4) #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } /**********showtime************/ const int maxn = 3e5+9; int le[maxn],ri[maxn]; vector<int>mp[maxn]; int id[maxn], dfn[maxn], tim = 0, sz[maxn]; void dfs(int u) { dfn[u] = ++tim; id[tim] = u; sz[u] = 1; for(int v : mp[u]) { dfs(v); sz[u] += sz[v]; } } struct node{ int le, ri, id, op; node(int Le, int Ri, int Id, int Op){ le = Le; ri = Ri; id = Id; op = Op; } }; vector<node> g[maxn]; ll ans[maxn]; struct TT{ ll sum[maxn<<2], lazy[maxn<<2]; void pushdown(int le, int ri, int rt) { lazy[rt<<1] += lazy[rt]; lazy[rt<<1|1] += lazy[rt]; int mid = (le + ri) >> 1; sum[rt<<1] += 1ll*lazy[rt] * (mid - le + 1); sum[rt<<1|1] += 1ll*lazy[rt] * (ri - mid); lazy[rt] = 0; } void update(int L, int R, int c, int le, int ri, int rt) { if(le >= L && ri <= R) { sum[rt] += 1ll * c * (ri - le + 1); lazy[rt] += 1ll * c; return ; } int mid = (le + ri) >> 1; if(lazy[rt]) pushdown(le , ri, rt); if(L <= mid) update(L, R, c, le, mid, rt<<1); if(mid < R) update(L, R, c, mid+1, ri, rt<<1|1); sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } ll query(int L, int R, int le, int ri, int rt) { if(le >= L && ri <= R) { return sum[rt]; } int mid = (le + ri) >> 1; if(lazy[rt]) pushdown(le, ri, rt); ll res = 0; if(L <= mid) res += query(L, R, le, mid, rt<<1); if(mid < R) res += query(L, R, mid+1, ri,rt<<1|1); return res; } } tree; int main(){ int n,m; scanf("%d%d", &n, &m); le[1] = 1, ri[1] = n; for(int i=1; i<=m; i++) { int u,v,l,r; scanf("%d%d%d%d", &u, &v, &l, &r); mp[u].pb(v); le[v] = l, ri[v] = r; } int N = m + 1; ///dfs序,将一个点的子树表示成一个区间 dfs(1); int q; scanf("%d", &q); for(int i=1; i<=q; i++) { int u, le, ri; scanf("%d%d%d", &u, &le, &ri); int t1 = dfn[u] - 1; ///将一次询问拆成两次操作,类似于把区间和改为前缀和相减 g[t1].pb(node(le, ri, i, -1)); g[t1 + sz[u]].pb(node{le, ri, i, 1}); } for(int i=1; i<=tim; i++) { int u = id[i]; tree.update(le[u], ri[u], 1, 1, n, 1); ///因为是二维面积,所以要用线段树等数据结构维护前缀和 for(node a : g[i]) { ans[a.id] += 1ll * a.op * tree.query(a.le, a.ri, 1, n, 1); } } for(int i=1; i<=q; i++) printf("%lld ", ans[i]); return 0; } /* 4 3 1 2 1 2 1 3 2 4 3 4 2 3 2 1 1 4 3 1 4 */
J - Just Jump
由于有m个限制,我们就计算出m个对应点不合法方案的个数。
这里要利用容斥,第i个点不合法的方案要减去之前就不合法的点转移过来的方案。
然后dp转移。
O($m ^ 2 + n$)的复杂度
从一个点x转移到y,走p步,每步长度要大于t,的方案数。可以转化为小球放入盒子中的问题。
设x到y之间有n个点,
就可以转化为有n个小球,分到p个盒子中,每个盒子至少要有t个小球。
那么我们先安排每个盒子t个小球。
那么剩下的$ n - t imes p$个小球放入盒子中,盒子可以为空。
这个怎么做呢,我们利用隔板法,多放入$ n - t imes p$个盒子,选出 p - 1个盒子作为隔板即可。
即
$dbinom{n - p imes t + p - 1}{p-1}$
// #pragma GCC optimize(2) // #pragma GCC optimize(3) // #pragma GCC optimize(4) #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } /**********showtime************/ const int maxn=1e7+10; ll A[maxn]; ll B[maxn]; ll quick(int x,int n){ ll ans=1; while(n){ if(n&1) ans=1ll*ans*x%mod; x=1ll*x*x%mod; n=n/2; } return ans; } void init(){ int n=maxn-1; A[0]=1; B[0]=1; for(int i=1;i<=n;i++) A[i]=1ll*A[i-1]*i%mod; B[n]=quick(A[n],mod-2); for(int i=n-1;i>=1;i--) B[i]=1ll*B[i+1]*(i+1)%mod; } ll CC(ll n,ll x){ if(x>n) return 0; return 1ll*A[n]*B[n-x]%mod*B[x]%mod; } ll dp[maxn]; ll s[3009], sum[maxn]; pii a[3009]; int main(){ init(); // debug(CC(500, 1)); int L,d,m; scanf("%d%d%d", &L, &d, &m); for(int i=1; i<=m; i++) { scanf("%d%d", &a[i].se, &a[i].fi); } sort(a + 1, a + 1 + m); for(int i=1; i<=m; i++) { ll pos = a[i].fi, t = a[i].se; s[i] = CC(pos - t * d + t - 1, t - 1); // cout<<s[i]<<" "; for(int j=1; j<i; j++){ if(a[j].fi < a[i].fi && a[j].se < a[i].se) { ll prepos = a[j].fi, pret = a[j].se; ll n = pos - prepos, tt = t - pret; s[i] = (s[i] - 1ll*s[j] * CC(n - tt * d + tt - 1, tt - 1)%mod + mod) % mod; } } dp[pos] =((dp[pos] - s[i])% mod + mod) % mod; } // cout<<endl; dp[0] = 1; sum[0] = 1; for(int i=1; i<=L; i++) { if(i-d >= 0) dp[i] += sum[i-d]; dp[i] = dp[i] % mod; sum[i] = (sum[i-1] + dp[i]) % mod; // cout<<dp[i]<<" "; } // cout<<endl; printf("%lld ", dp[L]); return 0; } /* 5 2 4 1 2 2 5 2 8 2 9 */