Day 1 :
T1 : 期末考试
很水的一道题,但是自己搞了大半天过不了大样例.
最后还A了...
主要思想就是枚举最后一个完成的任务的时间
然后对两部分的代价分类讨论统计一下。
(考试代码,略丑)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline void read(int &x){ x=0;char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; } inline void read(ll &x){ x=0;char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; } #define rg register int #define rep(i,a,b) for(rg i=(a);i<=(b);++i) #define per(i,a,b) for(rg i=(a);i>=(b);--i) const int maxn = 100010; ll A,B,C;int n,m; int t[maxn],b[maxn]; namespace work1{ int maxtim = 0; #define lowbit(x) ((x)&(-x)) struct Bit{ ll c[maxn]; inline void modify(int x,int val){ for(;x <= maxtim;x += lowbit(x)) c[x] += val; } inline ll query(int x){ ll ret = 0; for(;x;x-=lowbit(x)) ret += c[x]; return ret; } }sit,sut,sib,sub; int main(){ ll sum_t = 0,sum_b = 0; rep(i,1,n){ read(t[i]); maxtim = max(maxtim,t[i]); sum_t += t[i]; } rep(i,1,m){ read(b[i]); maxtim = max(maxtim,b[i]); sum_b += b[i]; } rep(i,1,n) sit.modify(t[i],1),sut.modify(t[i],t[i]); rep(i,1,m) sib.modify(b[i],1),sub.modify(b[i],b[i]); ll ans = (1LL<<60); ll res,lsum_t = 0,rsum_t = sum_t,lsum_b = 0,rsum_b = sum_b; int lsiz_t = 0,rsiz_t = n,lsiz_b = 0,rsiz_b = m; rep(i,1,maxtim){ lsiz_t = sit.query(i);rsiz_t = n - lsiz_t; lsum_t = sut.query(i);rsum_t = sum_t - lsum_t; lsiz_b = sib.query(i);rsiz_b = m - lsiz_b; lsum_b = sub.query(i);rsum_b = sum_b - lsum_b; res = C*(1LL*i*lsiz_t - lsum_t); ll lc = 1LL*i*lsiz_b - lsum_b; ll rc = rsum_b - 1LL*i*rsiz_b; if(B <= A){ res += rc*B; }else{ if(rc >= lc){ res += lc*A; rc -= lc; }else{ res += rc*A; rc = 0; } if(rc != 0){ res += rc*B; rc = 0; } } ans = min(ans,res); } printf("%lld\n",ans); return 0; } } namespace work2{ int main(){ int tim = 0; rep(i,1,n) read(t[i]); rep(i,1,m){ read(b[i]); tim = max(b[i],tim); } ll ans = 0; rep(i,1,n){ if(t[i] >= tim) continue; ans += (tim - t[i])*C; } printf("%lld\n",ans); return 0; } } namespace work3{ int main(){ int minn = 1000000; rep(i,1,n){ read(t[i]); minn = min(minn,t[i]); } ll can = 0,nd = 0; rep(i,1,m){ read(b[i]); if(b[i] < minn) can += minn - b[i]; if(b[i] > minn) nd += b[i] - minn; } ll res = 0; if(B <= A) res += nd*B; else{ if(can >= nd){ res += nd*A; nd = 0; }else{ res += can*A; nd -= can; } if(nd != 0) res += nd*B; } printf("%lld\n",res); return 0; } } int main(){ read(A);read(B);read(C); read(n);read(m); if(A == 1000000000 && B == 1000000000) work2::main(); else if(C == 10000000000000000LL) work3::main(); else work1::main(); return 0; }
T2 : 相逢是问候
我们发现每个位置上的值在修改操作中是作为幂出现的.
所以我们不能直接将序列 % p进行维护.
考虑欧拉定理.那么应该 % 上的数应该是phi(p)
或者phi(phi(p))或phi(phi(phi(p)))什么的.
打表观察规律后可以发现一个数最多被phi() log次就会变成1.
所以预处理出来不断phi(p)得到的所有不为1的数.
可以通过维护每个点被修改操作覆盖了多少次来剪枝.
对于最小的被覆盖次数也大于得到的区间就可以直接跳出了.
所以可以用线段树实现这个过程.
复杂度是... \(O(n\log^3 n)\) !??!?!?!?
不要问我是怎么跑过去的.
UPD : 在bzoj 上听闻管理员把我叉掉了...
然后swm_sxt也说我的代码被叉掉了...
毕姥爷的题解这么说的啊...我是照着题解写的啊...
然后上知乎得知毕姥爷被叉掉了.
... ...
代码已更正.可以过掉目前bzoj上的数据.
(其实就多了一句 ph[++cntp] = 1 )...QAQ
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline void read(int &x){ x=0;static char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; } #define rg register int #define rep(i,a,b) for(rg i=(a);i<=(b);++i) #define per(i,a,b) for(rg i=(a);i>=(b);--i) const int maxn = 50010; inline int phi(int x){ int ret = x; for(rg i=2;i*i<=x;++i){ if(x % i == 0){ ret /= i;ret *= i-1; while(x % i == 0) x /= i; } } if(x ^ 1) ret /= x,ret *= x-1; return ret; } int ph[maxn],cntp; int n,m,p,c; int a[maxn]; struct Node{ int val,mn; Node(){val = mn = 0;} friend Node operator + (const Node &a,const Node &b){ Node c; c.val = (a.val + b.val) % ph[0]; c.mn = min(a.mn,b.mn); return c; } }T[maxn<<2]; int query(int rt,int l,int r,int L,int R){ if(L <= l && r <= R) return T[rt].val; int mid = l+r >> 1; if(R <= mid) return query(rt<<1,l,mid,L,R); if(L > mid) return query(rt<<1|1,mid+1,r,L,R); return (query(rt<<1,l,mid,L,R) + query(rt<<1|1,mid+1,r,L,R)) % ph[0]; } inline int qpow(int x,int p,int mod){ int ret = 1; for(;p;p>>=1,x=1LL*x*x%mod) if(p&1) ret=1LL*ret*x%mod; return ret; } inline int calc(int x,int t){ int ret = x; if(ret >= ph[t]) ret = ret % ph[t] + ph[t]; per(i,t,1){ x = ret; ret = qpow(c,x,ph[i-1]); if(x && !ret) ret += ph[i-1]; } return ret % ph[0]; } void build(int rt,int l,int r){ if(l == r){ T[rt].val = calc(a[l],0); return ; } int mid = l+r >> 1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); T[rt] = T[rt<<1] + T[rt<<1|1]; } void modify(int rt,int l,int r,int L,int R){ if(T[rt].mn >= cntp) return ; if(l == r){ T[rt].mn ++ ; T[rt].val = calc(a[l],T[rt].mn); return ; } int mid = l+r >> 1; if(L <= mid) modify(rt<<1,l,mid,L,R); if(R > mid) modify(rt<<1|1,mid+1,r,L,R); T[rt] = T[rt<<1] + T[rt<<1|1]; } int main(){ read(n);read(m);read(p);read(c); ph[cntp = 0] = p; while(p != 1) ph[++ cntp] = (p = phi(p)); ph[++ cntp] = 1; rep(i,1,n) read(a[i]); build(1,1,n); int opt,l,r; while(m--){ read(opt);read(l);read(r); if(opt == 1) printf("%d\n",query(1,1,n,l,r)); else modify(1,1,n,l,r); } return 0; }
T3 : 组合数问题
这道题考场上直接Lucas上的.
这道题应该没有办法从数论的角度上来进行优化.
考虑一下这些组合数的具体含义:
注意 : \(r < k\))不要问我为什么要强调注意这个.
那么这些组合数的含义可以这么理解:
从共n*k个物品中选出%k为r的数目的物品的方案数.
这样就很明显了.设\(f[i][j]\)表示前i个数中选出%k为j的数目的物品的方案数.
矩乘优化dp转移.\(O(k^3\log n)\)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline void read(ll &x){ x=0;static char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; } #define rg register int #define rep(i,a,b) for(rg i=(a);i<=(b);++i) #define per(i,a,b) for(rg i=(a);i>=(b);--i) ll p; struct Matrix{ ll n,m; ll s[52][52]; void clear(ll n,ll m){ this->n = n;this->m = m; memset(s,0,sizeof s); } friend Matrix operator * (const Matrix &a,const Matrix &b){ Matrix c;c.clear(a.n,b.m); rep(i,0,c.n-1) rep(j,0,c.m-1) rep(k,0,a.m-1){ c.s[i][j] += 1LL*a.s[i][k]*b.s[k][j] % p; if(c.s[i][j] >= p) c.s[i][j] -= p; }return c; } }; ll n,k,r; Matrix A,B; int main(){ read(n);read(p);read(k);read(r); A.clear(1,k);A.s[0][0] = 1; B.clear(k,k); rep(i,0,k-1){ B.s[i][i] += 1; B.s[(i-1+k)%k][i] += 1; } ll x = 1LL*n*k; for(;x;x>>=1,B=B*B) if(x&1) A=A*B; printf("%lld\n",A.s[0][r]); return 0; }
Day2:
T1 :
暂时还不会,挖坑.
T2 : 分手是祝愿
首先考虑对于一个确定的局面如何确定最优策略.
通过很简单的分析可以发现 : 只要从高位到低位确定即可.
然后通过等价变化,将枚举因数变为枚举倍数,可以做到O(nlogn)求出最优解.
通过上面的过程,我们可以得到一个推论 : 最优方案是唯一的.
应该被操作的位是固定的.
所以在进行dp时我们采用如下定义方式 : 设f[i]表示当前的操作中有i个是正确的.
那么通过对下一步的随意选取,很容易得到方程:\(f_i = \frac{n-i}{n}*f_{i+1} + \frac{i}{n}*f_{i-1}\)
然后这个东西直接利用系数递推进行转移即可.
设\(f_i = B_i*f_{i-1} + C_i\)
那么
\[f_i = \frac{n-i}{n}*(B_{i+1}*f_i+C_{i+1}) + \frac{i}{n}*f_{i-1} + 1\]
\[(1 - \frac{n-i}{n}B_{i+1})f_i = \frac{i}{n}f_{i-1} + \frac{n-i}{n}C_{i+1} + 1\]
\[f_i = \frac{\frac{i}{n}f_{i-1} + \frac{n-i}{n}C_{i+1} + 1}{(1 - \frac{n-i}{n}B_{i+1})}\]
可以得到递推关系 :
- \(B_i = \frac{\frac{i}{n}}{(1 - \frac{n-i}{n}B_{i+1})}\)
- \(C_i = \frac{\frac{n-i}{n}C_{i+1} + 1}{(1 - \frac{n-i}{n}B_{i+1})}\)
所以线性计算出系数再线性带入计算即可.
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline void read(int &x){ x=0;char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; } #define rg register int #define rep(i,a,b) for(rg i=(a);i<=(b);++i) #define per(i,a,b) for(rg i=(a);i>=(b);--i) const int mod = 100003; const int maxn = 100005; int a[maxn],tag[maxn],b[maxn],c[maxn],f[maxn]; inline int qpow(int x,int p){ int ret = 1; for(;p;p>>=1,x=1LL*x*x%mod) if(p&1) ret=1LL*ret*x%mod; return ret; } int main(){ int n,k;read(n);read(k); rep(i,1,n) read(a[i]); int s = 0; per(i,n,1){ int res = a[i]; for(int j=i*2;j<=n;j+=i) res^=tag[j]; tag[i] = res; s += res; } if(s <= k){ rep(i,1,n) s = 1LL*s*i%mod; printf("%d\n",s); }else{ b[k] = 0;c[k] = f[k] = k; b[n] = c[n] = 1; for(int i=n-1,inv;i>k;--i){ b[i] = i;c[i] = ( 1LL*(n-i)*c[i+1] % mod + n)%mod; inv = qpow( (n-1LL*(n-i)*b[i+1]%mod + mod) % mod,mod-2); b[i] = 1LL*b[i]*inv % mod; c[i] = 1LL*c[i]*inv % mod; } for(int i=k+1;i<=s;++i){ f[i] = (1LL*b[i]*f[i-1] + c[i])%mod; } int ans = f[s]; rep(i,1,n) ans=ans*1ll*i%mod; printf("%d\n",ans); } return 0; }
T3 : 寿司餐厅
说多了都是泪.
临考前考到了一道模型相似的题.
抱着这题肯定能A的心态去写的.
然后最大权闭合子图没调出来.
不说那么多了... ...
首先这道题是网络流没跑.
重点我们需要解决三个限制 :
- 选择区间互相的包含关系
- 不同寿司的第一次取用的额外代价
- 区间与寿司的对应关系.
我们使用最小割模型.
先考虑第一种限制,我们知道一个区间一定会对这个区间的所有子区间进行限制.
但是肯定不允许我们对所有子区间都进行连边限制
我们发现限制是具有传递性的,所以对于区间[l,r]直接向[l+1,r],[l,r-1]限制即可.
对于第二、三种限制,我们对于每个寿司建一个额外节点,连出流量为\(mx^2\),向汇点的边.
对于所有有用到过这个寿司的区间向对应的寿司节点连边.
这样完成了限制二、三.
然后处理处理细节就好了.具体的细节是什么.. ..
我懒得写了==
(这份代码在速度榜上目前rank1,如果按运行时间从大到小排序的话)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline void read(int &x){ x=0;static char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; } #define rg register int #define rep(i,a,b) for(rg i=(a);i<=(b);++i) #define per(i,a,b) for(rg i=(a);i>=(b);--i) const int maxn = 128; const int maxnode = maxn*maxn; const int maxedge = maxnode*10; const int inf = 0x3f3f3f3f; struct Edge{ int to,next,cap; }G[maxedge<<1]; int head[maxnode],cnt = 1; void add(int u,int v,int c){ G[++cnt].to = v; G[cnt].next = head[u]; head[u] = cnt; G[cnt].cap = c; } inline void insert(int u,int v,int c){ add(u,v,c);add(v,u,0); } #define v G[i].to int q[maxnode],l,r,dis[maxnode]; int S,T; bool bfs(){ memset(dis,-1,sizeof dis); dis[S] = 0;l = 0;r = -1; q[++r] = S; while(l <= r){ int u = q[l++]; for(int i = head[u];i;i=G[i].next){ if(dis[v] == -1 && G[i].cap){ dis[v] = dis[u] + 1; q[++r] = v; } } }return dis[T] != -1; } int dfs(int u,int f){ if(u == T || f == 0 ) return f; int ret = 0; for(int i = head[u];i;i=G[i].next){ if(dis[v] == dis[u] + 1 && G[i].cap){ int x = dfs(v,min(G[i].cap,f)); ret += x;f -= x; G[i].cap -= x; G[i^1].cap += x; if(f == 0) break; } }return ret; } inline int dinic(){ int ret = 0; while(bfs()) ret += dfs(S,inf); return ret; } #undef v int nodecnt = 0; int a[maxn],id[maxn][maxn],idx[maxn]; int main(){ int n,m;read(n);read(m); S = ++ nodecnt;T = ++ nodecnt; int mx = 0; rep(i,1,n) read(a[i]),mx = max(mx,a[i]); rep(i,1,mx){ idx[i] = ++ nodecnt; insert(idx[i],T,m*i*i); } int x,ans = 0; rep(i,1,n) rep(j,i,n) id[i][j] = ++ nodecnt; rep(i,1,n){ rep(j,i,n){ read(x); if(i == j) x -= a[i],insert(id[i][j],idx[a[i]],inf); else{ if(id[i+1][j])insert(id[i][j],id[i+1][j],inf); if(id[i][j-1])insert(id[i][j],id[i][j-1],inf); } if(x > 0) ans += x,insert(S,id[i][j],x); else insert(id[i][j],T,-x); } } ans -= dinic(); printf("%d\n",ans); return 0; }
来源:https://www.cnblogs.com/Skyminer/p/6758563.html