2018 icpc 徐州
A
矩阵树定理可以用于最小生成树计数,最直观的做法就是求个mst,再用矩阵树定理求最小生成树个数,但是n<=1e5,显然不是o(n^3)可以做出来的。
考虑随机数据生成器,固定1e5的边,但是边权在unsigned long long的范围内随机指定,由样例看出,即使是点数很少的情况下,最多也只有一个生成树
于是,我们猜测,只需要做一遍最小生成树,并假定不会有更多的生成树就可以了。
#include<bits/stdc++.h> #define ull unsigned long long using namespace std; const int maxn = 100050; const ull mod = 1000000007; int f[maxn]; int findf(int x){ return f[x] == x ? x : f[x] = findf(f[x]); } int n,m; ull k1,k2; struct edge{ int u; int v; ull w; friend bool operator < (edge a,edge b){ return a.w < b.w; } }e[maxn]; ull xorS(){ ull k3 = k1,k4 = k2; k1 = k4; k3 ^= k3 << 23; k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26); return k2 + k4; } int main(){ int T; cin>>T; while(T--){ cin>>n>>m>>k1>>k2; for(int i = 1;i <= m;i++){ e[i].u = xorS() % n + 1; e[i].v = xorS() % n + 1; e[i].w = xorS(); } sort(e+1,e+1+m); for(int i = 1;i <= n;i++){ f[i] = i; } ull tot = 0; int uu,vv,cnt = 0; for(int i = 1;i <= m;i++){ uu = e[i].u; vv = e[i].v; uu = findf(uu); vv = findf(vv); if(uu == vv) continue; else{ cnt++; e[i].w %= mod; tot = (tot + e[i].w) % mod; f[uu] = vv; } } if(cnt != n-1) tot = 0; cout<<tot<<endl; } return 0; }
G
选择k个路径,它们至少有一个交点,首先先把路径经过的点标记一下,对于一个点,它被选中,至少要有k个路径经过它。
这样就有一个需要考虑的地方,如果这k个路径有不止一个交点,就会被重复统计,如何去重?
两条路径的交点,只可能是树上连续的一段,所以多条路径的交点,也只能是树上的一段,对于一个方案,可以只在深度最小的那个点统计一次,而这个点,肯定是某个路径深度最小的点。
遍历每个点,新加进去以这个点为lca的路径,和经过这个点的其他路径联合起来算对方案的贡献。
如何快速计算一个点经过多少路径?可以让深度最深的点+1,走出这条路径或者达到交点(lca)时-1,也就是树上差分。
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #define fo(i, l, r) for (long long i = l; i <= r; i++) #define fd(i, l, r) for (long long i = r; i >= l; i--) #define mem(x) memset(x, 0, sizeof(x)) #define ll long long #define ld double using namespace std; const int maxn = 300050; const ll mod = 1e9 + 7; ll read() { ll x = 0, f = 1; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }; while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'); ch = getchar(); }; return x * f; } int n, m, k; vector<int> g[maxn]; int d[maxn]; int fa[maxn][25]; void dfs(int u, int f, int deep) { d[u] = deep; fa[u][0] = f; int t = 0; while (fa[u][t] && fa[fa[u][t]][t]) { fa[u][t + 1] = fa[fa[u][t]][t]; t++; } int sz = (int)g[u].size() - 1, v; fo(i, 0, sz) { v = g[u][i]; if (v == f) continue; dfs(v, u, deep + 1); } } int lca(int u, int v) { if (d[u] < d[v]) swap(u, v); int delta = d[u] - d[v]; int t = 0; while (delta) { if (delta & 1) u = fa[u][t]; t++; delta >>= 1; } if (u == v) return u; t = 0; while (t >= 0) { if (fa[u][t] != fa[v][t]) { u = fa[u][t]; v = fa[v][t]; t++; } else { t--; } } return fa[u][0]; } int addamt[maxn], cf[maxn], uu[maxn], vv[maxn]; ll fac[maxn], inv[maxn]; ll ans; ll C(ll n, ll m) { return fac[n] * inv[m] % mod * inv[n - m] % mod; } void dfs2(int u) { int v, sz = (int)g[u].size() - 1; fo(i, 0, sz) { v = g[u][i]; if (v == fa[u][0]) continue; dfs2(v); cf[u] += cf[v]; } } int main() { //freopen("data.in","r",stdin); fac[0] = fac[1] = 1; fo(i, 2, maxn - 1) { fac[i] = (fac[i - 1] * i) % mod; } inv[0] = inv[1] = 1; fo(i, 2, maxn - 1) { inv[i] = (mod - (mod / i)) * inv[mod % i] % mod; } fo(i, 2, maxn - 1) { inv[i] = (inv[i] * inv[i - 1]) % mod; } int T = read(); while (T--) { memset(fa, 0, sizeof(fa)); memset(d, 0, sizeof(d)); ans = 0; n = read(); m = read(); k = read(); fo(i, 1, n) g[i].clear(); int u, v; fo(i, 1, n - 1) { u = read(); v = read(); g[u].push_back(v); g[v].push_back(u); } fo(i, 1, m) { uu[i] = read(); vv[i] = read(); } dfs(1, 0, 1); fo(i, 1, n) cf[i] = addamt[i] = 0; int zz, cd; fo(i, 1, m) { zz = lca(uu[i], vv[i]); addamt[zz]++; cf[uu[i]]++; cf[vv[i]]++; cf[zz]--; cf[fa[zz][0]]--; } dfs2(1); fo(i, 1, n) { cf[i] -= addamt[i]; fo(j, 1, addamt[i]) { if (j > k) break; if (cf[i] >= k - j){ ans = (ans + C(cf[i], k - j) * C(addamt[i], j) % mod) % mod; } } } printf("%lld\n", ans); } return 0; }
H
如果区间的数量不大于颜色数,那么每个区间染上不同的颜色就可以了。
如果有断层,尽量染上缺少的颜色,如果区间比较多,如何确保染到一个最优的颜色?
某些颜色在大于某个位置就没有了,如果染上那个最早失效的颜色,就可能会使答案增大。
对于每个颜色,维护一个它消失的位置,每次取出那个消失位置最靠前的染色。
这个题spj有毛病,行尾有空格算你wa,是真的无语。
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #define fo(i, l, r) for (long long i = l; i <= r; i++) #define fd(i, l, r) for (long long i = r; i >= l; i--) #define mem(x) memset(x, 0, sizeof(x)) #define ll long long #define ld double using namespace std; const int maxn = 200050; const ll mod = 1e9 + 7; ll read() { ll x = 0, f = 1; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }; while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'); ch = getchar(); }; return x * f; } struct dat{ int p; ll v; friend bool operator < (dat a,dat b){ return a.v > b.v; } }now,nxt,col; priority_queue<dat> q,ys; ll n,k,ans[maxn],op[maxn],ed[maxn],cnt[maxn]; ll tot; int main() { int T=read(); while(T--){ tot=0; while(!q.empty())q.pop(); while(!ys.empty())ys.pop(); n=read();k=read(); if(k>n)k=n; fo(i,1,n)ans[i]=0; fo(i,1,k)cnt[i]=0; int nowp=0,nowk=0; fo(i,1,n){ now.p=i; now.v=read(); q.push(now); op[i] = now.v; now.v=read(); q.push(now); ed[i] = now.v; } fo(i,1,k){ now.p=i; now.v=-1; ys.push(now); } while(!q.empty()){ now=q.top(); q.pop(); if(ans[now.p]){ cnt[ans[now.p]]--; if(cnt[ans[now.p]]==0) nowk--; }else{ col=ys.top(); ys.pop(); ans[now.p] = col.p; cnt[col.p]++; if(cnt[col.p]==1) nowk++; col.v = max(col.v,(ll)ed[now.p]); ys.push(col); } nowp = now.v; if(!q.empty()){ nxt=q.top(); if(nxt.v == now.v) continue; else{ if(nowk >= k) tot += nxt.v - now.v; } } } printf("%lld\n",tot); printf("%lld",ans[1]); fo(i,2,n) printf(" %lld",ans[i]); putchar('\n'); } return 0; }
M
首先预处理每个灯照亮的边,它是一段连续的区间。
能不能照亮某个边,需要 点与边的两个端点的连线不经过凸包内部,可以利用叉积快速判断,逆时针方向下只要点在边的右边就可以。
处理之后,要做一个线段覆盖,由于是环,需要做n次
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <set> #include <cmath> #include <queue> #include <map> #include <ctime> #define ll long long #define ld double #define lson rt << 1, l, m #define pi acos(-1) #define rson rt << 1 | 1, m + 1, r #define fo(i, l, r) for (int i = l; i <= r; i++) #define fd(i, l, r) for (int i = r; i >= l; i--) #define mem(x) memset(x, 0, sizeof(x)) #define eps 1e-11 using namespace std; const ll maxn = 2050; const ll mod = 998244353; ll read() { ll x = 0, f = 1; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }; while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'); ch = getchar(); }; return x * f; } int sgn(double x) { if (fabs(x) < eps) return 0; if (x < 0) return -1; else return 1; } struct Point { double x, y; Point() {} Point(double _x, double _y) { x = _x; y = _y; } double operator^(const Point &b) const { return x * b.y - y * b.x; } double operator*(const Point &b) const { return x * b.x + y * b.y; } Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); } } pts[maxn],bulb[maxn]; struct Line { Point s, e; Line() {} Line(Point _s, Point _e) { s = _s; e = _e; } int linecrossseg(Line v) { int d1 = sgn((e - s) ^ (v.s - s)); int d2 = sgn((e - s) ^ (v.e - s)); if ((d1 ^ d2) == -2) return 2; return (d1 == 0 || d2 == 0); } int relation(Point p) { int c = sgn((p - s) ^ (e - s)); if (c < 0) return 1; else if (c > 0) return 2; else return 3; } bool parallel(Line v) { return sgn((e - s) ^ (v.e - v.s)) == 0; } int linecrossline(Line v) { if (this->parallel(v)) return v.relation(s) == 3; return 2; } Point crosspoint(Line v) { double a1 = (v.e-v.s) ^ (s-v.s); double a2 = (v.e-v.s) ^ (e-v.s); return Point((s.x * a2-e.x * a1) / (a2-a1), (s.y * a2-e.y * a1) / (a2-a1)); } }A,B; int n,m; int lp[maxn],rp[maxn]; int rec[maxn],tmp[maxn]; int ans,tot; struct dat{ int p,l,r; friend bool operator < (dat a,dat b){ if(a.l != b.l) return a.l < b.l; else return a.r > b.r; } }dats[maxn]; int main() { int T=read(); while(T--){ n=read();m=read(); fo(i,1,m) lp[i]=rp[i]=0; fo(i,1,n){ pts[i].x=read(); pts[i].y=read(); pts[n+i] = pts[i]; } fo(i,1,m){ bulb[i].x=read(); bulb[i].y=read(); bool lst=false,cur=false; rp[i]=n; fo(j,1,n){ A = Line(pts[j],pts[j+1]); if(A.relation(bulb[i])!=2)cur=false; else cur=true; if(!lst&&cur) lp[i]=j; if(lst&&!cur){ rp[i]=j-1; } lst = cur; } if(rp[i]<lp[i])rp[i]+=n; dats[i].l=lp[i];dats[i].r=rp[i];dats[i].p=i; } sort(dats+1,dats+1+m); ans = m + 1; fo(t,1,n){ tot = 0; int pt = t,pp,vv=-1; fo(i,1,m){ tmp[i]=false; } fo(i,1,m){ if(pt >= t+n)break; if(dats[i].l > pt){ pt = vv + 1; tot++; tmp[pp]=true; } if(dats[i].l<=pt&&dats[i].r>=pt){ if(vv<dats[i].r){ pp=dats[i].p; vv=dats[i].r; } } } if(pt<t+n && vv>=t+n-1){ pt = vv+1; tot++; tmp[pp]=true; } if(pt>=t+n&&ans>tot){ ans=tot; fo(i,1,m) rec[i]=tmp[i]; } } if(ans==m+1){ printf("-1\n"); continue; } printf("%d\n",ans); bool fst=true; fo(i,1,m){ if(rec[i]){ if(fst) fst=false; else putchar(' '); printf("%d",i); } } putchar('\n'); } return 0; }
I
要构造一个最长上升子序列长度至少为n-1的序列,可以让一个数移动到另一个数后边,这样就有o(n^2)个
每个序列,经过排序之后,若符合条件,那么一定在这些目标序列之内。
可以倒着枚举哪些排序器起到了作用。
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #define fo(i, l, r) for (long long i = l; i <= r; i++) #define fd(i, l, r) for (long long i = r; i >= l; i--) #define mem(x) memset(x, 0, sizeof(x)) #define ll long long #define ld double using namespace std; const int maxn = 200050; const ll mod = 1e9 + 7; ll read() { ll x = 0, f = 1; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }; while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'); ch = getchar(); }; return x * f; } struct dat{ int v[55]; }now,nxt,dats[maxn]; int n,m,cnt; int a[50],b[50]; ll q,ans; void dfs(int u,int d){ if(d > m){ ans++; return; } int aa=a[m-d+1],bb=b[m-d+1]; if(dats[u].v[aa] > dats[u].v[bb]) return; dfs(u,d+1); swap(dats[u].v[aa],dats[u].v[bb]); dfs(u,d+1); swap(dats[u].v[aa],dats[u].v[bb]); } int main() { int T=read(); while(T--){ n=read(); m=read(); q=read(); cnt=ans=0; fo(i,1,m){ a[i]=read(); b[i]=read(); } fo(i,1,n){ dats[0].v[i]=i; } fo(i,1,n){ fo(j,0,n){ now = dats[0]; if(i==j||i==j+1||i==j-1)continue; if(j<i){ int tmp = i; fd(k,j+1,i-1){ swap(now.v[k],now.v[k+1]); } now.v[j+1]=tmp; }else{ int tmp = i; fo(k,i+1,j){ swap(now.v[k],now.v[k-1]); } now.v[j]=tmp; } cnt++; dats[cnt]=now; } } cnt++; dats[cnt] = dats[0]; fo(i,1,cnt){ dfs(i,1); } ans %= mod; printf("%lld\n",ans); } return 0; }