理解否 我尝试和你握手 心平气和地交流 别害怕曾经出丑 我不走
t4 是 P11945,前面三题简单爬不到懒得找了
test3
答答题sol
本可看错题了搞了半天我不行了。
完全图 \(i\to j\) 的边权是 \(|2^i-2^j|\),贡献是 \(|s_i-s_j|\),要求边权单调递增的路径的最大贡献和。
要求边权单调递增又边权严格不相等,那么边两两之间的前后顺序一定,设 \(dis_u\) 表示以 \(u\) 结尾的最长路,可以考虑按照边权从小到大去取边更新。问题在于因为空间限制边存不下来,氮素考虑到 \(2^i\) 量级差很大直接先枚举 \(i=1\to n\) 再枚举 \(j=i-1\to 1\) 就单调惹,严谨地说 \(2^i>2^i-2^j\geq 2^{i-1}\)。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=5005, inf=1e13;
int T, n, s[N], tag[N], f[N], Ans;
void mian() {
cin >> n, Ans=-inf;
up(i,1,n) f[i]=0;
up(i,1,n) cin >> tag[i];
up(i,1,n) cin >> s[i];
up(i,1,n) dn(j,i-1,1) if(tag[i]!=tag[j]) {
int fi=f[i], fj=f[j];
f[i]=max(f[i],fj+abs(s[i]-s[j]));
f[j]=max(f[j],fi+abs(s[i]-s[j]));
}
up(i,1,n) Ans=max(Ans,f[i]);
cout << Ans << '\n';
}
signed main() {
freopen("sol.in","r",stdin);
freopen("sol.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while(T--) mian();
return 0;
}
金币coin
嗯大概就是 \(1+ki\) 看起来很不顺眼吧不妨令编号变成 \(0,\dots,n-1\),这样子每一轮去掉 \(k\) 的倍数位置上的人,所以一轮的位置变化形如 \(x\to x-\lfloor \frac{x}{k}\rfloor-1\)。然后有一个观察就是只有 \(ik\) 和 \(ik-1\) 这样变化之后的值相同。
正着推很难想想怎么倒过来,对于 \(y \to x\) 有 \(y-\lfloor \frac{y}{k}\rfloor-1=x\),考虑到相同的值只有两个直接乘出来取小的就可以惹,嗯对就是 \(\lfloor\frac{xk}{k-1}\rfloor+1\to x\),注意到暴力可以做轮数不多的就是 \(k\leq \sqrt m\) 的了,后面只用考虑怎么做 \(k>\sqrt m\)。
\(k>\sqrt m\) 的话显然 \(\lfloor\frac{x}{k}\rfloor\leq \sqrt m\) (其实带等号是xp),不妨考虑将增量相等的放在一起做,从小到大考虑增量 \(i\) 要加 \(Ans\) 次,只需要考虑一边的不等式 \(\lfloor\frac{x+Ans\times i}{k}\rfloor+1=i\),至少有一个 \(Ans\leq \frac{(i-1)k-x}{i}\),这个时候我们会困惑要是算出来 \(k|x+Ans\times i\) 怎么办惹,氮素注意到在两个位置都能被表示的前提下尽量多加 \(ik-1\) 肯定比 \(ik\) 优先就不困惑惹。
最后就是 lgj oj 对我来讲有点卡常,可以调 \((k=2)\sqrt m\) 为界限,再加上火车头。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
int T, n, m, k;
void mian() {
cin >> n >> k, m=sqrt(n)/2;
if(k<=m) {
int now=0, cnt=0;
__int128 p;
while((p=(__int128)now*k/(k-1)+1)<n) {
if(++cnt>1e7) {
assert(0);
}
now=p;
}
cout << now+1 << '\n';
}
if(k>m) {
int now=0;
up(i,1,n/m) {
int l=(n-now+i-1)/i-1, r=(i*k-now+i-1)/i-1;
if(l<r) now+=i*l; else now+=i*r;
}
cout << now+1 << '\n';
}
}
signed main() {
freopen("coin.in","r",stdin);
freopen("coin.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while(T--) mian();
return 0;
}
围棋go
设选手 \(i\) 的能力值分别为 \(rk_{i,1/2/3}\),现在考虑选手 \(u\) 是否能成为冠军。
对于选手 \(i,j\),如果分别有 \(rk_{i,1/2/3}<rk_{j,1/2/3}\),那么 \(j\) 不能直接打败 \(i\),反之亦然。然后 \(i,j\) 的关系除了偏序还可以是能互相打败,对此关系连边,有一个观察,在一个联通块中谁都能成为这个连通块的冠军,具体而言可以随便拉一棵生成树,以目标冠军为根从下往上打败。容易发现在计算出了连通块之后,不同连通块之间肯定是都三维偏序的否则会有 \(rk\) 的交叉,现在只用考虑 \(u\) 是否在 \(rk\) 最大的那个连通块中间。
现在问题变成对题目中的三个数组 \(a/b/c\) 查找一个最小的前缀 \(p\),满足 \(\{a/b/c_1,\dots,a/b/c_p\}\) 素相同的集合,也就是要求满足 \(rk_{i,1/2/3}\leq p\) 的 \(i\) 有 \(p\) 个惹,那么对于每一个 \(i\) 可以贡献给位置 \(\max\{rk_{i,1/2/3}\},\dots,n\) 对应的前缀咯,用线段树维护 \(sum_p-p\) 的最大值然后线段树上二分即可求出 \(p\)。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
using namespace std;
const int N=100005;
int n, q, rd, a[N][4], tr[N<<2], tag[N<<2];
inline void tup(int p) {
tr[p]=max(tr[ls(p)],tr[rs(p)]);
}
inline void tdn(int p) {
tag[ls(p)]+=tag[p], tr[ls(p)]+=tag[p];
tag[rs(p)]+=tag[p], tr[rs(p)]+=tag[p];
tag[p]=0;
}
void build(int p=1,int s=1,int e=n) {
if(s==e) {
tr[p]=-s;
return;
}
int mid=(s+e)>>1;
build(ls(p),s,mid);
build(rs(p),mid+1,e);
tup(p);
}
void upt(int l,int r,int v,int p=1,int s=1,int e=n) {
if(l<=s&&e<=r) {
tag[p]+=v, tr[p]+=v;
return;
}
tdn(p);
int mid=(s+e)>>1;
if(l<=mid) upt(l,r,v,ls(p),s,mid);
if(r>mid) upt(l,r,v,rs(p),mid+1,e);
tup(p);
}
int query(int p=1,int s=1,int e=n) {
if(s==e) return s;
tdn(p);
int mid=(s+e)>>1;
if(tr[ls(p)]>=0) return query(ls(p),s,mid);
return query(rs(p),mid+1,e);
}
inline int Max(int i) {
return max(max(a[i][1],a[i][2]),a[i][3]);
}
void add(int i) {
upt(Max(i),n,1);
}
void del(int i) {
upt(Max(i),n,-1);
}
signed main() {
freopen("go.in","r",stdin);
freopen("go.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> q, build();
up(j,1,3) up(i,1,n) cin >> rd, a[rd][j]=i;
up(i,1,n) add(i);
while(q--) {
int opt, x, y, p;
cin >> opt;
if(opt==1) {
cin >> x;
if(Max(x)<=query()) cout << "DA\n";
else cout << "NE\n";
}
if(opt==2) {
cin >> p >> x >> y;
del(x), del(y);
swap(a[x][p],a[y][p]);
add(x), add(y);
}
}
return 0;
}
图好数tu
本可要晕惹。
先给当前点 \(u\) 分类,用 \(x\to y\) 表示 \(x\) 可以走到 \(y\),这里 \(x=y\) 素允许的,数字表示某个类型的点,\(s/t\) 表示起点终点。
对于每一类点给出必须/可以连的边有什么。
-
1 类点:\(s\) 能到,能到 \(t\)
必须要有 \(1\to u\to 1\),可以有 \(u\to 2,3\to u\)
-
2 类点:只有 \(s\) 能到
必须要有 \(1/2\to u\),可以有 \(u\to 2,3\to u\)
-
3 类点:只有能到 \(t\) 或者 什么都没有 /wq
可以有 \(u\to 1,3\to u,u\to 3,u\to 2\)
边类型有 \(1\to1 ,1\to 2,3\to 1,2\to 2,3\to 2,3\to 3\),考虑一下每条边的贡献分到前还是后面的点考虑,以及那么看看哪些可以独立出来算降低难度。
-
\(1\to 1\) 可以独立出来,反正 1 类点全都联通。要求大小为 \(k=1,\dots,n\) 的每个点都有 \(s\to u\to t\) 的图的数量。(独立出来只需要知道 \(k\) !)
计数肯定是希望找到简单的充要条件的,首先容易想到保证 \(i(\neq t)\) 都有出度可以保证 \(i\to t\),氮素有些点根本从 \(s\) 走不到,考虑强制 \(i(\neq s)\) 有入度,这样子所有的点都有 能从 \(s\) 到且能去 \(t\) 惹,因为前后肯定都可以一直走。想要 \(i(\neq t)\) 有出度只要 dp 的时候保证有向后的连边就可以惹,设 \(dp[i][j]\) 表示有 \(i\) 个点入度为 \(0\) 的点有 \(j\) 个,发现转移要枚举 \(dp[i-1][k]\) 且分别要乘二元的组合数复杂度被卡在 \(O(n^3)\)。考虑二项式反演,把恰好转成钦定,设 \(g[i][j]\) 表示 \(i\) 个点钦定 \(j\) 个点入度为 \(0\)(包括 \(s\))的方案数,\(i\) 个点的答案 \(D[i]=\sum_{j=1}^i (-1)^{j-1}\binom{i}{j} dp[i][j]\),然后递推素考虑一下以前的起点要不要钦定有 \(dp[i][j]=(2^{i-j}-1)dp[i-1][j-1]+(2^{i-j}-1)dp[i-1][j]\),嗯对。
-
因为对于 2 类点 \(u\),\(1/2\to 2\) 是必要的,所以 \(1\to 2,2\to 2\) 被迫一起考虑惹。然后剩下 \(3\to 1/2/3\),都在 \(3\) 那里做贡献设 \(3\) 的位置集合为 \(\{p\}\),这些贡献是 \(\prod_{p_i}2^{n-p_i}\),又因为 \(1/2\to 2\) 关系的不是具体位置而是前面某类点有几个,\(3\to 1/2/3\) 的贡献也能被本可独立出来了,具体而言假设有 \(c=|p|\) 个 \(3\),去 dp 贡献 \(\sum_{|p|=c}\prod_{p_i}2^{n-p_i}\) 乘起来即可,\(g[n][c]\) 求起来是简单 dp 设 \(g[i][j]\) 表示考虑了 \(1,\dots,i\) 选了 \(j\) 个的贡献,转移是背包考虑选不选然后乘上贡献,就素 \(s,t\) 不能选。那么只剩下考虑 \(1/2\to 2\) 的方案数惹,也素直接背包就行了,设计 \(f[i][j]\) 表示考虑了 \(i\) 个有 \(j\) 个 1 类 \(i-j\) 个 2 类这种,还素 \(s,t\) 是 1。
最后合并一下贡献,枚举有 \(i\) 个一类点,\(j\) 个二类点,\(n-i-j\) 个三类点,\(D_i\times g[n][n-i-j]\times f[i+j][i]\) 贡献到 $ Ans_i$。
嗯还有就是 \(Ans_0=Ans_2\),就素有没有 \(1\to n\) 的双射罢惹。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=2005;
int n, P, dp[N][N], D[N], f[N][N], g[N][N], pw[N], yh[N][N], Ans[N];
inline void add(int &a,int b) { a=(a+b)%P; }
inline int C(int x,int y) {
if(x<y||y<0) return 0;
return yh[x][y];
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> P;
pw[0]=1;
up(i,1,n) pw[i]=2*pw[i-1]%P;
up(i,0,n) {
yh[i][0]=yh[i][i]=1;
up(j,1,i-1) yh[i][j]=(yh[i-1][j-1]+yh[i][j])%P;
}
dp[1][1]=1;
up(i,2,n) up(j,1,i) {
add(dp[i][j],(pw[i-j]-1)%P*dp[i-1][j-1]%P);
add(dp[i][j],(pw[i-j]-1)%P*dp[i-1][j]%P);
}
up(i,1,n) up(j,1,i) add(D[i],(j%2==1?1:-1)*C(i,j)%P*dp[i][j]%P);
f[1][1]=1;
up(i,2,n) up(j,1,i) {
add(f[i][j],f[i-1][j-1]);
add(f[i][j],(pw[i-1]-1)%P*f[i-1][j]%P);
}
dn(i,n,2) up(j,1,i) f[i][j]=f[i-1][j-1];
g[1][0]=1;
up(i,2,n) up(j,0,i) {
add(g[i][j],g[i-1][j]);
if(j) add(g[i][j],pw[n-i]*g[i-1][j-1]%P);
}
dn(i,n,2) up(j,1,i) g[i][j]=g[i-1][j];
up(i,1,n) up(j,0,n-i) add(Ans[i],D[i]*f[i+j][i]%P*g[n][n-i-j]%P);
Ans[0]=Ans[2];
up(i,0,n) cout << (Ans[i]%P+P)%P << ' ';
return 0;
}

浙公网安备 33010602011771号