断网练习 1
Day 1
一道难题
一遍过捏
点击查看代码
struct node{
int v,w;
};
int n,root,m;
vector<node> e[N];
int w[N];
int dp[N];
bool st[N];
void dfs(int u,int fa,int qwq) {
dp[u] = qwq;
bool flag = false;
int sum = 0;
for (auto it : e[u]) {
int v = it.v,w = it.w;
if (v == fa) continue;
flag = true;
dfs(v,u,w);
sum += dp[v];
}
if (flag)
dp[u] = min(dp[u],sum);
}
int main(){
n = fr(),root = fr();
int a,b,w;
m = n - 1;
while (m --) {
a = fr(),b = fr(),w = fr();
e[a].push_back({b,w});
e[b].push_back({a,w});
}
dfs(root,0,inf);
fw(dp[root]);
return 0;
}
跳舞的线
这里要注意的就是说他这个初始化的时候不能把所有第一行和第一列全部初始化为
然后就是这个地方要注意一下特判起点是 # 的情况就可以了
点击查看代码
int n,m;
int dp[N][N][2];
//0是竖着,1是横着
bool flag[N][N];
int main(){
n = fr(),m = fr();
string s;
for (int i = 1; i <= n; i ++) {
cin >> s;
for (int j = 0; j < m; j ++) {
if (s[j] == '#')
flag[i][j + 1] = true;
}
}
memset(dp,-0x3f,sizeof dp);
dp[1][1][0] = dp[1][1][1] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
if (flag[i][j]) continue;
if (i == 1 || j == 1) {
if (i == 1 && j == 1) continue;
if (i == 1) dp[i][j][1] = dp[i][j - 1][1];
if (j == 1) dp[i][j][0] = dp[i - 1][j][0];
continue;
}
if (!flag[i - 1][j]) {
dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j][1] + 1);
}
if (!flag[i][j - 1]) {
dp[i][j][1] = max(dp[i][j - 1][1],dp[i][j - 1][0] + 1);
}
}
}
int ans = max(dp[n][m][0],dp[n][m][1]);
memset(dp,0x3f,sizeof dp);
dp[1][1][0] = dp[1][1][1] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
if (flag[i][j]) continue;
if (i == 1 || j == 1) {
if (i == 1 && j == 1) continue;
if (i == 1) dp[i][j][1] = dp[i][j - 1][1];
if (j == 1) dp[i][j][0] = dp[i - 1][j][0];
continue;
}
if (!flag[i - 1][j]) {
dp[i][j][0] = min(dp[i - 1][j][0],dp[i - 1][j][1] + 1);
}
if (!flag[i][j - 1]) {
dp[i][j][1] = min(dp[i][j - 1][1],dp[i][j - 1][0] + 1);
}
}
}
if (flag[1][1]) {
wj;
return 0;
}
if (dp[n][m][1] >= inf / 2 && dp[n][m][0] >= inf / 2) {
wj;
return 0;
}
fw(ans),kg;
fw(min(dp[n][m][0],dp[n][m][1]));
return 0;
}
Day 2
排兵布阵
一遍过捏
点击查看代码
int n,m,s;
int dp[N][M];
int w[N][N];
int main(){
s = fr(),n = fr(),m = fr();
for (int i = 1; i <= s; i ++) {
for (int j = 1; j <= n; j ++) {
w[j][i] = fr();
}
}
for (int i = 1; i <= n; i ++) {
sort(w[i] + 1,w[i] + 1 + s);
}
memset(dp,-0x3f,sizeof dp);
memset(dp[0],0,sizeof dp[0]);
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= m; j ++) {
dp[i][j] = max(dp[i - 1][j],dp[i][j]);
for (int k = 1; k <= s; k ++) {
if (j > w[i][k] * 2)
dp[i][j] = max(dp[i - 1][j - w[i][k] * 2 - 1] + k * i,dp[i][j]);
else break;
}
}
}
fw(dp[n][m]);
return 0;
}
玩具取名
区间
而这里的
大的区间可以用小的区间转移,状态转移方程如下:
意思是当
点击查看代码
int n = 4;
int w[N];
char c[N];
string s;
bool flag[5][5][5];//j,k => i
bool dp[N][N][5];//k => i,j
int get(char i) {
if (i == 'W') return 1;
if (i == 'I') return 2;
if (i == 'N') return 3;
if (i == 'G') return 4;
return 19920929;
}
int main(){
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= w[i]; j ++) {
scanf("%s",(c + 1));
flag[i][get(c[1])][get(c[2])] = true;
}
}
cin >> s;
int len = s.length();
for (int i = 0; i < len; i ++) {
dp[i][i][get(s[i])] = true;
}
for (int cd = 2; cd <= len; cd ++) {
for (int l = 0; l < len; l ++) {
int r = l + cd - 1;
if (r >= len) break;
for (int k = l; k < r; k ++) {
for (int t = 1; t <= 4; t ++) {
for (int a = 1; a <= 4; a ++) {
for (int b = 1; b <= 4; b ++) {
dp[l][r][t] |= flag[t][a][b] & dp[l][k][a] & dp[k + 1][r][b];
}
}
}
}
}
}
bool st = false;
if (dp[0][len - 1][1]) {
st = true;
cout << "W";
}
if (dp[0][len - 1][2]) {
st = true;
cout << "I";
}
if (dp[0][len - 1][3]) {
st = true;
cout << "N";
}
if (dp[0][len - 1][4]) {
st = true;
cout << "G";
}
if (!st) wj;
return 0;
}
Day 3
4084 Barn Painting G
一遍过捏
点击查看代码
int n,k;
vector<int> e[N];
int col[N];
lwl dp[N][4];
void dfs(int u,int fa) {
if (col[u]) {
dp[u][col[u]] = 1;
} else
dp[u][1] = dp[u][2] = dp[u][3] = 1;
for (auto v : e[u]) {
if (v == fa) continue;
dfs(v,u);
dp[u][1] = (lwl)dp[u][1] * (dp[v][2] + dp[v][3]) % mod;
dp[u][2] = (lwl)dp[u][2] * (dp[v][1] + dp[v][3]) % mod;
dp[u][3] = (lwl)dp[u][3] * (dp[v][1] + dp[v][2]) % mod;
}
}
int main(){
n = fr(),k = fr();
int a,b;
for (int i = 1; i < n; i ++) {
a = fr(),b = fr();
e[a].push_back(b);
e[b].push_back(a);
}
while (k --) {
a = fr(),b = fr();
col[a] = b;
dp[a][b] = 1;
}
dfs(1,0);
lwl ans = (lwl)dp[1][1] + dp[1][2] + dp[1][3];
ans %= mod;
fw(ans);
return 0;
}
8903 Bribing Friends G
这个地方就是要用一些奇怪的贪心,假设已经确定要选一些牛,那么肯定把冰淇淋给那些可以兑换更多钱的牛为优,所以要么都用,要么就是其它都用剩余的都用在这头牛上面。
然后按照每头牛所需要的冰淇淋的大小顺序从小到大排序,预处理一下到第
但是
点击查看代码
struct node{
int p,c,x;
};
int n,A,B;
node w[N];
int dp[N][N],f[N][N];
//到第i头牛,剩了j个冰淇淋。
//到第i头牛,剩了j块钱
//dp从前往后,f从后往前.
bool cmp(node a,node b) {
return a.x < b.x;
}
int main(){
n = fr(),A = fr(),B = fr();
int ww,yy,hh;
for (int i = 1; i <= n; i ++) {
ww = fr(),yy = fr(),hh = fr();
w[i] = {ww,yy,hh};
}
sort(w + 1,w + 1 + n,cmp);
for (int i = 1; i <= n; i ++) {
int p = w[i].p,c = w[i].c,x = w[i].x;
for (int j = 0; j <= B; j ++)
dp[i][j] = dp[i - 1][j];
for (int j = 0; j + x * c <= B; j ++)
dp[i][j + x * c] = max(dp[i][j + x * c],dp[i - 1][j] + p);
}
for (int i = n; i; i --) {
int p = w[i].p,c = w[i].c;
for (int j = 0; j <= A; j ++)
f[i][j] = f[i + 1][j];
for (int j = 0; j + c <= A; j ++) {
f[i][j + c] = max(f[i][j + c],f[i + 1][j] + p);
}
}
lwl ans = 0;
for (int i = 1; i <= n; i ++) {
int p = w[i].p,c = w[i].c,x = w[i].x;
ans = max(ans,dp[i][B] + f[i + 1][A]);
ans = max(ans,dp[i - 1][B] + f[i][A]);
for (int j = 0; j <= min(A,c); j ++) {
if (x * (c - j) > B) continue;
ans = max(ans,dp[i - 1][B - x * (c - j)] + f[i + 1][A - j] + p);
}
}
fw(ans);
return 0;
}
Day 4
5468 回家路线
这一题一开始写的时候爆搜有八十分我很震惊,爆搜交上去没过的点是
wanqitzh找到问题了,
就是当有这种情况的时候,按照数字的顺序遍历边,就会使得到3的时候dp[u]已经变了
正解(不完全)是根据每个站点出发的时间排一个序,然后从前往后遍历一遍所有车次,处理出到第
点击查看代码
struct node{
int v,be,en;
};
struct Node{
int u,v,be,en;
};
int n,m,A,B,C;
Node w[N * 2];
int dp[N][1005];
int ans = inf;
bool cmp(Node a,Node b) {
return a.be < b.be;
}
int get(int q,int p) {
return A * (p - q) * (p - q) + B * (p - q) + C;
}
int main(){
n = fr(),m = fr(),A = fr(),B = fr(),C = fr();
int a,b,be,en;
for (int i = 1; i <= m; i ++) {
a = fr(),b = fr(),be = fr(),en = fr();
w[i] = {a,b,be,en};
}
sort(w + 1,w + 1 + m,cmp);
memset(dp,0x3f,sizeof dp);
dp[1][0] = 0;
for (int i = 1; i <= m; i ++) {
a = w[i].u,b = w[i].v,be = w[i].be,en = w[i].en;
for (int j = 0; j <= be; j ++) {
dp[b][en] = min(dp[b][en],dp[a][j] + get(j,be));
}
}
for (int i = 0; i <= 1000; i ++) {
ans = min(dp[n][i] + i,ans);
}
fw(ans);
return 0;
}
Day 5
4933 大师
一遍过捏
点击查看代码
int n;
int w[N];
int dp[N][V * 2 + 2];
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
int ans = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j < i; j ++) {
int t = w[i] - w[j] + V;
dp[i][t] = (dp[i][t] + dp[j][t] + 1) % mod;
ans = (ans + (dp[j][t]) + 1) % mod;
}
}
fw(ans + n);
return 0;
}
6349 樱花,还有你
前缀和
建议是下次直接开
点击查看代码
int n,k;
int dp[N];
int w[N];
int sum[N];
int main(){
n = fr(),k = fr();
int Sum = 0;
for (int i = 1; i <= k; i ++) {
w[i] = fr();
Sum += w[i];
}
if (Sum < n) {
wj;
return 0;
}
dp[0] = 1;
int ans = 0;
for (int i = 1; i <= k; i ++) {
sum[0] = 1;
for (int j = 1; j <= n; j ++) {
sum[j] = (sum[j - 1] + dp[j]) % mod;
}
for (int j = 1; j <= n; j ++) {
dp[j] = dp[j];
int t = sum[j - 1] - sum[max(j - w[i],1) - 1];
t %= mod;
if (j - w[i] <= 0) t ++;
dp[j] = (dp[j] + t) % mod;
}
ans = (ans + dp[n]) % mod;
}
fw((ans + mod) % mod);
return 0;
}
6040 课后期末考试滑溜滑溜补习班
单调队列优化。
一开始
点击查看代码
int n,k,d,x,tp;
int w[N];
int Seed;
int dp[N];
inline int rnd () {
static const int MOD = 1e9;
return Seed = ( 1LL * Seed * 0x66CCFF % MOD + 20120712 ) % MOD;
}
signed main(){
n = fr(),k = fr(),d = fr(),x = fr(),tp = fr();
if (!tp) {
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
} else {
Seed = fr();
for (int i = 1; i <= n; i ++) {
w[i] = rnd();
}
}
dp[1] = w[1];
deque<int> q;
q.push_front(1);
for (int i = 2; i <= n; i ++) {
if (q.size() && i - q.front() > x) {
q.pop_front();
}
dp[i] = dp[q.front()] + k + (i - q.front() - 1) * d + w[i];
while (q.size()&&dp[q.back()] + (i - q.back()) * d >= dp[i])
q.pop_back();
q.push_back(i);
}
fw(dp[n]);
return 0;
}
3961 黄金矿工
一遍过捏
点击查看代码
struct node{
int x,y,t,v;
};
int n,tim;
node w[N];
int dp[N][T][2];
bool cmp(node a,node b) {
if (a.x * b.y != a.y * b.x){
return a.x / a.y < b.x / b.y;
}
return a.y < b.y;
}
int main(){
n = fr(),tim = fr();
int a,b,t,v;
for (int i = 1; i <= n; i ++) {
a = fr(),b = fr(),t = fr(),v = fr();
w[i] = {a,b,t,v};
}
sort(w + 1,w + 1 + n,cmp);
memset(dp,-0x3f,sizeof dp);
int ans = -inf;
dp[0][0][0] = dp[0][0][1] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= tim; j ++) {
dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j][1]);
if (j < w[i].t) continue;
int t = w[i].t,v = w[i].v;
if (w[i].x * w[i - 1].y != w[i].y * w[i - 1].x){
dp[i][j][1] = max(dp[i - 1][j - t][0],dp[i - 1][j - t][1]) + v;
}
else dp[i][j][1] = dp[i - 1][j - t][1] + v;
ans = max(ans,dp[i][j][1]);
}
}
fw(ans);
return 0;
}
6323 ZBRKA
这种排列计数的一个套路是设
这里设
考虑填第
直接做复杂度是
其中
时间复杂度
点击查看代码
int n,m;
int dp[M][2],sum[M][2];
int main(){
n = fr(),m = fr();
dp[0][1] = sum[0][1] = 1;
for (int i = 1; i <= m; i ++) {
sum[i][1] = 1;
}
for (int i = 2; i <= n; i ++) {
dp[0][i & 1] = sum[0][i & 1] = 1;
for (int j = 1; j <= m; j ++) {
int w = j - i;
dp[j][i & 1] = sum[j][1 - (i & 1)];
if (w > -1)
dp[j][i & 1] = (dp[j][i & 1] - sum[w][1 - (i & 1)]) % mod;
sum[j][i & 1] = (sum[j - 1][i & 1] + dp[j][i & 1]) % mod;
}
}
int ans = (dp[m][n & 1] + mod) % mod;
fw(ans);
return 0;
}
Day 6
5343 分块
一遍过捏
点击查看代码
lwl n,b,x;
unordered_map<int,int> w;
set<int> s;
lwl dp[N];
lwl a[105][105];
bool flag[105];
void mul1(lwl ans[],lwl a[],lwl b[][105]) {
lwl temp[105];
memset(temp,0,sizeof temp);
for (int i = 1; i <= 100; i ++) {
for (int j = 1; j <= 100; j ++) {
temp[i] = (lwl)(temp[i] + (lwl)a[j] * b[j][i]) % mod;
}
}
memcpy(ans,temp,sizeof temp);
}
void mul2(lwl ans[][105],lwl a[][105],lwl b[][105]) {
lwl temp[105][105];
memset(temp,0,sizeof temp);
for (int i = 1; i <= 100; i ++) {
for (int j = 1; j <= 100; j ++) {
for (int k = 1; k <= 100; k ++) {
temp[i][j] = (temp[i][j] + (lwl)a[i][k] * b[k][j]) % mod;
}
}
}
memcpy(ans,temp,sizeof temp);
}
int main(){
n = fr();
b = fr();
int x;
for (int i = 1; i <= b; i ++) {
x = fr();
w[x] = true;
}
b = fr();
for (int i = 1; i <= b; i ++) {
x = fr();
if (w[x]) {
s.insert(x);
flag[x] = true;
}
}
dp[0] = 1;
for (int i = 1; i <= 100; i ++){
a[i][i + 1] = 1;
if(flag[i])
a[i][1] = true;
}
for (int i = 1; i <= 100; i ++) {
for (auto w : s) {
if (i < w) continue;
dp[i] = (dp[i] + dp[i - w]) % mod;
}
}
if (n <= 100) {
fw(dp[n]);
return 0;
}
n -= 100;
for (int i = 1; i <= 50; i ++) {
swap(dp[i],dp[101 - i]);
}
while (n) {
if (n & 1) mul1(dp,dp,a);
mul2(a,a,a);
n >>= 1;
}
fw(dp[1]);
return 0;
}
4677 山区建小学
看题解做的,没看题解之前基本只写了个输入()
具体分析基本都在代码里面写了,也比较好懂(),就不再写一遍了。这个题目在断网做的时候脑子里一直想的是区间 dp ,然后没想到怎么通过左右区间那个点来转移,于是寄
点击查看代码
int n,m;
int w[N];
int dp[N][N];//考虑前 i 个村庄,用了 j 个小学的最小距离和
int dis[N][N];//在第 i 个到第 j 个村庄之间只用 1 个小学的最小距离和
int main(){
n = fr(),m = fr();
int ans = inf;
for (int i = 2; i <= n; i ++) {
w[i] = fr();
ans = min(ans,w[i]);
w[i] += w[i - 1];
}
if (n == m) {
cout << 0 << endl;
return 0;
} if (n == m + 1) {
cout << ans << endl;
return 0;
}
// 预处理 dis 数组,感性理解一下,在这个区间的时候就是在最中间那个村庄的距离和最小
// 具体证明课参考小学奥数题捏
for (int l = 1; l <= n; l ++) {
for (int r = l; r <= n; r ++) {
int mid = (l + r) >> 1;
for (int k = l; k <= r; k ++) {
dis[l][r] += abs(w[k] - w[mid]);
}
}
}
for (int i = 0; i <= n; i ++) {
for (int j = 0; j <= m; j ++) {
dp[i][j] = inf;//初始化
}
}
dp[0][0] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= min(m,i); j ++) {
for (int k = j - 1; k <= i; k ++) {
dp[i][j] = min(dp[i][j],dp[k][j - 1] + dis[k + 1][i]);
}
}
}
fw(dp[n][m]);
return 0;
}
6622 信号传递
这个题断网的时候dfs写挂了,极其生气。
直接感性理解吧,麻了。(明天写了那种看得懂再说吧)
找到问题了,
朴素状压
状压的时候是从前往后面转移的。状压的时候就先找到现在有了几个
然后如果
点击查看代码
struct node{
int t;
int w[N];
};
int n,m,k;
int w[100005];
int cnt[N][N];
int id[1 << N],dp[1 << N];
int h[1 << N];
int ans = inf;
int main(){
n = fr(),m = fr(),k = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
w[i] --;
}
//预处理
for (int i = 1; i < n; i ++)
cnt[w[i]][w[i + 1]] ++;
queue<node> q;
node be;
be.t = 0;
for (int i = 0; i < m; i ++) {
be.w[i] = 0;
for (int j = 0; j < m; j ++) {
if (j == i) continue;
be.w[i] -= cnt[i][j];
be.w[i] += k * cnt[j][i];
}
}
q.push(be);
int M = (1 << m) - 1;
for (int i = 0; i < m; i ++)
id[1 << i] = i;
for (int i = 1; i <= M; i ++) {
dp[i] = inf;
h[i] = h[i >> 1] + (i & 1);
}
while (q.size()) {
auto u = q.front();
q.pop();
int p = h[u.t] + 1;
int udp,ut;
for (int j = M ^ u.t; j; j -= (j & -j)) {
// 枚举 u.t 的补集里面的一个点 i
int i = id[j & (- j)];
udp = dp[u.t],ut = (u.t | (1 << i));
udp += p * u.w[i];
dp[ut] = min(dp[ut],udp);
}
for (int i = 0; i < m; i ++) {
if ((u.t >> i) & 1) break;
node v;
v.t = (u.t | (1 << i));
for (int y = M ^ v.t; y; y -= (y & -y)) {
// 枚举 v.t 的补集里面的一个点 j
int j = id[y & -y];
v.w[j] = u.w[j] + (k * cnt[j][i] + cnt[i][j]) + cnt[j][i] - k * cnt[i][j];
}
q.push(v);
}
}
fw(dp[M]);
return 0;
}
Day 7
6649 Grid
由题意可以知道,第一次进入某一个位置的时候只能从右面或者下面进入。
然后考虑一种情况,进入某一个格子
然后
点击查看代码
int n,m;
lwl w[N][N];
lwl dp[N][N];
int sum[N][N];
//当第一次进入某一个位置的时候只能从它的右侧或者下侧进入
//(若该位置为(i,r),则只能从它的下侧进入,否则只能从它的右侧进入)
//下面考虑一种情况:
//进入某一格a[i][j]之后,可以先向左移动若干格,之后再回到a[i][j],如果这若
//干个格子中的数字之和小于零,那么dp[i][j]就应该加上它得到一个更优的解。
//设b[i][j]为a[i][j]加上左边若干个格子中的数字之后得到的最小值
//这里其实直接在 a 数组上修改即可,详情见代码
//b[i][j]=min(b[i][j-1],0)+a[i][j];;
//用得到的b数组计算dp的时候,由于b[i][j]的值可能已经包含b[i][j-1]的值
//(即b[i][j-1]b[i][j?1]小于零的情况),所以特殊判断这种情况即可。
int main(){
n = fr(),m = fr();
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
w[i][j] = fr();
sum[i][j] = sum[i][j - 1] + w[i][j];
}
//将w改为加上左边若干个格子中的数字之后的最小值
for (int j = 1; j <= m; j ++)
if (w[i][j - 1] < 0)
w[i][j] += w[i][j - 1];
}
for (int i = n; i; i --) {
for (int j = m + 1; j; j --) {
dp[i][j] = linf;
}
}
for (int i = n; i; i --) {
for (int j = m; j; j --) {
if (w[i][j] <= 0)
dp[i][j] = min(dp[i][j + 1],dp[i + 1][j] + w[i][j]);
else
dp[i][j] = min(dp[i][j + 1],dp[i + 1][j]) + w[i][j];
}
}
lwl ans = linf;
for (int i = 1; i <= m; i ++) {
ans = min(ans,dp[1][i]);
}
fw(ans);
return 0;
}
Day 8
6786 GCDs & LCMs
一遍过捏。数学推导在代码上面写了注释的
点击查看代码
int n;
int w[N];
set<int> t;
map<int,int> h;
map<int,int> cnt;
map<int,lwl> sum;
int find(int x) {
if (x != h[x])
h[x] = find(h[x]);
return h[x];
}
// bj < bi + bj + gcd(bi,bj) < 3bj
// lcm(bi,bj) = k * bj (k -> N* && 1 < k < 3)
// bi + bj + gcd(bi,bj) = lcm(bi,bj) = 2 * bj
// bi + gcd(bi,bj) = bj;
// bj - bi = gcd(bi,bj)
// lcm(bi,bj) = 2 * bj
// lcm(bi,bj) = bj * bi / gcd(bi,bj)
// bi = 2 * gcd(bi,bj);
// bj = 3 * gcd(bi,bj);
int main(){
n = fr();
lwl ans = -inf;
for (int i = 1; i <= n; i ++) {
w[i] = fr();
t.insert(w[i]);
cnt[w[i]] ++;
}
for (auto i : t) {
h[i] = i;
sum[i] = (lwl)i * cnt[i];
ans = max(ans,sum[i]);
}
for (auto u : t) {
if (u & 1) continue;
int v = u / 2 * 3;
if (!h[v]) continue;
sum[find(v)] += sum[find(u)];
h[find(u)] = h[find(v)];
ans = max(ans,sum[h[v]]);
}
fw(ans);
return 0;
}
4823 拯救小矮人
这里的
考虑:如果矮的不先走,那他以后有可能永远也走不掉了。高的如果先走,他对人梯的贡献就没了。所以可以得出:先走矮的。但如果两个人总长一样,应该把身子长的留下来,他对梯子的贡献更大。
点击查看代码
int n,H;
pii w[N];
int dp[N];
bool cmp(pii a,pii b) {
if (a.fi + a.se != b.fi + b.se)
return a.fi + a.se < b.fi + b.se;
return a.fi < b.fi;
}
int main(){
n = fr();
int a,b;
for (int i = 1; i <= n; i ++) {
a = fr(),b = fr();
w[i] = {a,b};
}
H = fr();
sort(w + 1,w + 1 + n,cmp);
memset(dp,-0x3f,sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; i ++) {
dp[0] += w[i].fi;
}
for (int i = 1; i <= n; i ++) {
for (int j = i; j; j --) {
if (dp[j - 1] + w[i].se >= H)
dp[j] = max(dp[j],dp[j - 1] - w[i].fi);
}
}
while (dp[n] < 0) n --;
fw(n);
return 0;
}
Day 9
8801 最大数字
一遍过捏
点击查看代码
lwl n,a,b;
int idx = 0;
int w[25];
lwl ans = -inf;
void dfs(int u,int a,int b,lwl sum) {
if (u > idx) {
ans = max(sum,ans);
return ;
}
if (w[u] + a >= 9 && w[u] + 1 - b <= 0) {
dfs(u + 1,a - 9 + w[u],b,sum * 10 + 9);
dfs(u + 1,a,b - w[u] - 1,sum * 10 + 9);
} else if (w[u] + a >= 9) {
dfs(u + 1,a - 9 + w[u],b,sum * 10 + 9);
} else if (w[u] + 1 <= b) {
dfs(u + 1,a,b - w[u] - 1,sum * 10 + 9);
} else {
dfs(u + 1,0,b,sum * 10 + w[u] + a);
}
}
int main(){
n = fr();
a = fr(),b = fr();
while (n) {
w[++ idx] = n % 10;
n /= 10;
}
for (int i = 1; i <= idx / 2; i ++) {
swap(w[i],w[idx - i + 1]);
}
dfs(1,a,b,0);
fw(ans);
return 0;
}
6722 village
如果图上有奇环,那么这就不是一个二分图。
然后从链入手分析,如果这张图上面有奇环的话,那么就一定有三元环(原图中奇环的头和尾以及中间的点,头和尾的距离 >
然后扩展一下,也可以说明如果存在奇环,那么一定存在三元环。然后证明若存在三元环,那么存在包括直径两个端点在内的三元环:
设
=>
=>
所以三元环上存在一点满足到
点击查看代码
int n,k;
vector<node> e[N];
int de[N],fa[N][18],dis[N];
int dist[1005][1005];
vector<int> edge[N];
int col[N];
void dfs(int u,int father) {
de[u] = de[father] + 1;
fa[u][0] = father;
for (int k = 1; k < log2(de[u]); k ++) {
fa[u][k] = fa[fa[u][k - 1]][k - 1];
}
for (auto it : e[u]) {
int v = it.v;
if (v == father) continue;
dis[v] = dis[u] + it.w;
dfs(v,u);
}
}
int LCA(int x,int y) {
if (de[x] < de[y]) swap(x,y);
int dh = de[x] - de[y];
int kmax = log2(dh);
for (int k = kmax; k >= 0; k --) {
if ((dh >> k) & 1)
x = fa[x][k];
}
if (x == y) return x;
kmax = log2(de[y]);
for (int k = kmax; k >= 0; k --) {
if (fa[x][k] != fa[y][k]) {
x = fa[x][k];
y = fa[y][k];
}
}
return fa[x][0];
}
bool get(int u,int fa,int color) {
col[u] = color;
for (auto v : edge[u]) {
if (fa == u) continue;
if (!col[v]) {
if (!get(v,u,3 - color))
return false;
}
else if (col[v] == col[u]) return false;
}
return true;
}
int main(){
int T;
T = fr();
while (T --) {
n = fr(),k = fr();
if (n > 1000) {
wj;
continue;
}
for (int i = 1; i <= n; i ++) {
dis[i] = 0;
de[i] = 0;
e[i].clear();
col[i] = 0;
edge[i].clear();
}
memset(fa,0,sizeof fa);
int a,b,w;
for (int i = 1; i < n; i ++) {
a = fr(),b = fr(),w = fr();
e[a].push_back({b,w});
e[b].push_back({a,w});
}
dfs(1,0);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j < i; j ++) {
int p = LCA(i,j);
int t = dis[i] + dis[j] - 2 * dis[p];
if (t >= k) {
edge[i].push_back(j);
edge[j].push_back(i);
}
}
}
bool flag = true;
for (int i = 1; i <= n; i ++) {
if (!col[i]) {
if (!get(i,0,1)) {
flag = false;
break;
}
}
}
if (flag) yj;
else wj;
}
return 0;
}
6883 Kronican
一遍过捏
点击查看代码
int n,k;
int w[N][N];
int dp[1 << N];
// 1 => empty
int main(){
n = fr(),k = fr();
if (n == k) {
wj;
return 0;
}
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
w[i][j] = fr();
}
}
int maxn = (1 << n) - 1;
memset(dp,0x3f,sizeof dp);
dp[0] = 0;
int ans = inf;
for (int i = 0; i < maxn; i ++) {
int cnt = 0;
for (int j = 0; j < n; j ++) {
if ((i >> j) & 1) cnt ++;
}
if (n - cnt <= k) ans = min(ans,dp[i]);
for (int j = 0; j < n; j ++) {
if ((i >> j) & 1) continue;
for (int k = 0; k < n; k ++) {
if (((i >> k) & 1)) continue;
if (k == j) continue;
dp[i | (1 << j)] = min(dp[i | (1 << j)],dp[i] + w[j][k]);
}
}
}
fw(ans);
return 0;
}
Day 10
5851 Greedy Pie Eaters P
区间
然后dp方程就来乐:
点击查看代码
int n,m;
int dp[N][N]; // [i,j] finish max
int w[N][N][N]; // k i j
// eat k && i <= l <= k <= r <= j maxw
bool cmp(node a,node b) {
if (a.l != b.l) return a.l < b.l;
return a.r < b.r;
}
int main(){
m = fr(),n = fr();
int l,r,ww;
for (int i = 1; i <= n; i ++) {
ww = fr(),l = fr(),r = fr();
for (int j = l; j <= r; j ++) {
w[j][l][r] = ww;
}
}
for (int k = 1; k <= m; k ++) {
for (int i = k; i; i --) {
for (int j = k; j <= m; j ++) {
if (i != 1)
w[k][i - 1][j] = max(w[k][i - 1][j],w[k][i][j]);
if (j != m)
w[k][i][j + 1] = max(w[k][i][j + 1],w[k][i][j]);
}
}
}
for (int i = m; i; i --) {
for (int j = i; j <= m; j ++) {
for (int k = i; k < j; k ++) {
dp[i][j] = max(dp[i][j],dp[i][k] + dp[k + 1][j]);
}
for (int k = i; k <= j; k ++) {
dp[i][j] = max(dp[i][j],dp[i][k - 1] + dp[k + 1][j] + w[k][i][j]);
}
}
}
int ans = dp[1][m];
fw(ans);
return 0;
}
Day 11
7976 游园会
xx做的。
点击查看代码
lwl n;
lwl w[N],idx;
lwl f[3] = {1,2,1},p[4] = {0,1,3,4};
lwl h[N];
void init() {
h[1] = 1;
for (int i = 2; i <= 64; i ++) {
h[i] = h[i - 1] * 4 % mod;
}
}
int main(){
int T = fr(),maxn = fr();
maxn += 19920929;
init();
while (T --) {
n = fr();
lwl ans = 0,t = 1;
idx = 0,n ++;
while (n) {
w[++ idx] = n % 3;
n /= 3;
}
for (int i = idx; i; i --) {
ans = (ans + p[w[i]] * t * h[i]) % mod;
t = t * f[w[i]] % mod;
}
fw(ans);
ch;
}
return 0;
}
8848 1-1 B
(第三点是x <= y)
点击查看代码
int n,m;
int cnt[2];
lwl f[N],nf[N];
lwl dp[N],w[N];
lwl ksm(lwl a,lwl k) {
lwl ans = 1;
while (k) {
if (k & 1) ans = ans * a % mod;
a = a * a % mod;
k >>= 1;
}
return ans;
}
void init() {
f[0] = nf[0] = 1;
for (int i = 1; i <= n; i ++) {
f[i] = f[i - 1] * i % mod;
nf[i] = nf[i - 1] * ksm(i,mod - 2) % mod;
}
}
int main(){
n = fr();
init();
int a;
for (int i = 1; i <= n; i ++) {
a = fr();
if (a < 0) cnt[0] ++;
else cnt[1] ++;
}
if (cnt[1] == 0) {
wj;
return 0;
}
if (cnt[0] >= cnt[1]) {
lwl ans = f[cnt[0] + 1] * nf[cnt[0] + 1 - cnt[1]] % mod;
ans = ans * nf[cnt[1]] % mod;
fw(ans);
return 0;
}
m = cnt[1] - cnt[0];
dp[0] = 1;
for (int i = 1; i <= n; i ++) {
w[0] = dp[1],w[m] = dp[m - 1];
for (int j = 1; j < m; j ++) {
w[j] = (dp[j - 1] + dp[j + 1]) % mod;
}
memcpy(dp,w,sizeof w);
}
fw(dp[m]);
return 0;
}
Day 12
7925 童年
一开始断网写的时候,本来写的是对的,但是因为没有开
简直是戏剧性
点击查看代码
int n,ans;
int w[N];
int fa[N],d[N];
pii dp[N];
vector<int> e[N];
vector<int> yz;
priority_queue<pii,vector<pii>,greater<pii> > q;
void dfs(int u,int wu,int need) {
dp[u].se = w[u];
int t = need;
for (auto v : e[u]) {
dfs(v,wu + w[u],t);
if (dp[v].se < 0) continue;
need = max(need,dp[v].fi);
dp[u].se += dp[v].se;
}
if (u == 1) return ;
if (w[u] < 0) need = max(need,-w[u]);
else if (dp[u].se < 0) need = 0,dp[u].se = 0;
else need = max(0,need - w[u]);
dp[u].fi = need;
if (fa[u] == 1) {
q.push({dp[u].fi,dp[u].se});
}
}
signed main(){
n = fr(),ans = fr();
bool flag = false;
for (int i = 2; i <= n; i ++) {
fa[i] = fr();
e[fa[i]].push_back(i);
d[fa[i]] ++;
if (fa[i] != i - 1) flag = true;
}
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
if (!flag) {
int sum = ans;
for (int i = 1; i <= n; i ++) {
sum += w[i];
if (sum < 0) break;
ans = max(ans,sum);
}
fw(ans);
return 0;
}
dfs(1,0,0);
int t = ans;
if (ans < -w[1]) {
fw(ans);
return 0;
}
ans += w[1];
while (q.size()) {
auto t = q.top();
q.pop();
if (t.se < 0) continue;
if (t.fi <= ans) ans += t.se;
}
ans = max(ans,t);
fw(ans);
return 0;
}
Day 13
4362 贪吃的九头龙
这个要存一下
然后小小的分类讨论一下
点击查看代码
int n,m,k;
vector<node> e[N];
int dp[N][N][2];
int f[N][2];
int d[N];
void dfs(int u,int from) {
dp[u][0][0] = dp[u][1][1] = 0;
for (auto it : e[u]) {
int v = it.v,val = it.w;
if (v == from) continue;
dfs(v,u);
memcpy(f,dp[u],sizeof f);
memset(dp[u],0x3f,sizeof dp[u]);
for (int i = 0; i <= k; i ++) {
for (int j = 0; j <= i; j ++) {
int t = min(dp[v][j][0] + val,dp[v][j][1]);
dp[u][i][0] = min(dp[u][i][0],f[i - j][0] + t);
t = min(dp[v][j][1] + val,dp[v][j][0]);
dp[u][i][1] = min(dp[u][i][1],f[i - j][1] + t);
}
}
}
}
void dfs1(int u,int from) {
dp[u][0][0] = dp[u][1][1] = 0;
for (auto it : e[u]) {
int v = it.v,val = it.w;
if (v == from) continue;
dfs1(v,u);
memcpy(f,dp[u],sizeof f);
memset(dp[u],0x3f,sizeof dp[u]);
for (int i = 0; i <= k; i ++) {
for (int j = 0; j <= i; j ++) {
int t = min(dp[v][j][0],dp[v][j][1]);
dp[u][i][0] = min(dp[u][i][0],f[i - j][0] + t);
t = min(dp[v][j][1] + val,dp[v][j][0]);
dp[u][i][1] = min(dp[u][i][1],f[i - j][1] + t);
}
}
}
}
int main(){
n = fr(),m = fr(),k = fr();
if (m - 1 + k > n) {
wj;
return 0;
}
int a,b,w;
for (int i = 1; i < n; i ++) {
a = fr(),b = fr(),w = fr();
e[a].push_back({b,w});
e[b].push_back({a,w});
}
memset(dp,0x3f,sizeof dp);
if (m == 2) {
dfs(1,0);
}
else {
dfs1(1,0);
}
fw(dp[1][k][1]);
return 0;
}
3609 Hoof, Paper, Scissor G
也算是一遍过吧(),一开始有一次
点击查看代码
int n,k;
int w[N];
int dp[N][25][4];
int main(){
n = fr(),k = fr();
string s;
for (int i = 1; i <= n; i ++) {
cin >> s;
if (s == "P") w[i] = 1;
else if (s == "H") w[i] = 2;
else w[i] = 3;
}
memset(dp,-0x3f,sizeof dp);
dp[0][0][1] = dp[0][0][2] = dp[0][0][3] = 0;
int ans = 1;
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= k; j ++) {
dp[i][j][1] = dp[i - 1][j][1] + (w[i] == 3);
dp[i][j][2] = dp[i - 1][j][2] + (w[i] == 1);
dp[i][j][3] = dp[i - 1][j][3] + (w[i] == 2);
if (!j) continue;
int t = max(dp[i - 1][j - 1][2],dp[i - 1][j - 1][3]);
dp[i][j][1] = max(dp[i][j][1],t + (w[i] == 3));
t = max(dp[i - 1][j - 1][1],dp[i - 1][j - 1][3]);
dp[i][j][2] = max(dp[i][j][2],t + (w[i] == 1));
t = max(dp[i - 1][j - 1][2],dp[i - 1][j - 1][1]);
dp[i][j][3] = max(dp[i][j][3],t + (w[i] == 2));
ans = max(dp[i][j][1],max(dp[i][j][2],dp[i][j][3]));
}
for (int j = 0; j <= k; j ++)
ans = max(dp[i][j][1],max(dp[i][j][2],dp[i][j][3]));
}
fw(ans);
return 0;
}
Day 14
6103 直接自然溢出啥事没有
这个就是有一点细节要注意,就是说这个 值 和这个 函数 是会有重复的,所以说就要在转移的时候注释掉一点东西,然后不要注释错了!!这个题也不能从前往后转移,虽然我不知道为什么,但是不重要。
点击查看代码
int n;
lwl cxpd[N],yj[N],yjk[N],hs[N],zhi[N];
// ; , {;} => 程序片段
// ; , {;} , {} => 语句
// {} , {}{} , {;} , {{;}} => 语句块
// []{;} , [](){;} , ([]{;}) , ([](){;}) => 函数
// []{;} , [](){;} , []{;}() , [](){;}() => 值
// ([]{;}) , ([](){;}) , ([]{;}()) , ([](){;}()) => 值
// []{;}; , [](){;}; , []{;}(); , [](){;}(); => 语句
int main(){
n = fr();
cxpd[0] = 1;
yj[1] = 1;
cxpd[1] = 1;
for (int i = 2; i <= n; i ++) {
yjk[i] += cxpd[i - 2];
hs[i] += yjk[i - 2];
hs[i] += hs[i - 2];
yj[i] += yjk[i];
if (i >= 4)
hs[i] += yjk[i - 4];
zhi[i] += hs[i];
zhi[i] += zhi[i - 2];
yj[i] += zhi[i - 1];
for (int j = 1; j <= i; j ++) {
cxpd[i] += cxpd[i - j] * yj[j];
}
}
fw(cxpd[n]);
return 0;
}
Day 15
5607 网格图
也算是一遍过吧(?)。真是无语,一开始想着求稳把前面小的点用最小生成树暴力建边过,结果小的点全部都错了写的正解是对的。等会去看看是不是生成树哪里写错了。
点击查看代码
struct node{
lwl w,id;
bool flag;
// true => 一列的点
// false => 一行的点
};
int n,m;
node a[N * 2];
pii b[N];
int h[N];
lwl hang,lie;
int get(int x,int y) {
return (x - 1) * m + y;
}
bool cmp(node a,node b) {
return a.w < b.w;
}
int main(){
n = fr(),m = fr();
for (int i = 1; i <= n; i ++) {
a[i] = {fr(),i,1};
}
for (int i = 1; i <= m; i ++) {
b[i] = {fr(),i};
a[n + i] = {b[i].fi,i,0};
}
sort(a + 1,a + 1 + n + m,cmp);
lwl ans = 0;
lwl cnt = 0;
for (int i = 1; i <= n + m; i ++) {
int t = 0;
if (a[i].flag) {
if (lie) t = 1;
cnt += m - 1 - t * max(hang - 1,0);
ans += (lwl)(m - 1 - t * max(hang - 1,0)) * a[i].w;
lie ++;
} else {
if (hang) t = 1;
cnt += n - 1 - t * max(lie - 1,0);
ans += (lwl)(n - 1 - t * max(lie - 1,0)) * a[i].w;
hang ++;
}
if (cnt == (lwl)n * m - 1) break;
}
fw(ans),ch;
return 0;
}
3891 采集资源
晚上断网写的时候先写的网格图,然后感觉好困,就不想动脑子,就只写了一个
点击查看代码
int n,m,T;
int dp[N][N]; // 时间,效率
pii w[N];
int main(){
n = fr(),m = fr(),T = fr();
int a,b;
int idx = 0;
if (m > T) {
wj;
return 0;
}
for (int i = 1; i <= n; i ++) {
a = fr(),b = fr();
if (a > T) continue;
w[++ idx] = {a,b};
}
n = idx;
memset(dp,-0x3f,sizeof dp);
dp[0][0] = m;
for (int i = 0; ; i ++) {
for (int j = 0; j <= T; j ++) {
for (int k = 1; k <= n; k ++) {
a = w[k].fi,b = w[k].se;
if (a <= dp[i][j]) {
if (j + b < T) {
dp[i][j + b] = max(dp[i][j + b],dp[i][j] - a);
} else {
fw(i + 1);
return 0;
}
}
}
if (dp[i][j] + j >= T) {
fw(i + 1);
return 0;
} else dp[i + 1][j] = max(dp[i + 1][j],dp[i][j] + j);
}
}
return 0;
}
Day 16
4805 合并饭团
是一道区间
感觉区间
点击查看代码
int n;
int w[N];
int dp[N][N];
int main(){
n = fr();
int maxn = -inf;
bool flag = false;
int sum = 0;
for (int i = 1; i <= n; i ++) {
w[i] = fr();
sum += w[i];
maxn = max(w[i],maxn);
dp[i][i] = w[i];
if (i > 1 && w[i] == w[i - 1]) flag = true;
if (i > 2 && w[i] == w[i - 2]) flag = true;
}
if (!flag) {
fw(maxn);
return 0;
}
for (int len = 2; len <= n; len ++) {
for (int l = 1; l < n; l ++) {
int r = l + len - 1;
if (r > n) break;
for (int i = l; i < r; i ++) {
if (dp[l][i] && dp[l][i] == dp[i + 1][r]) {
dp[l][r] = dp[l][i] + dp[i + 1][r];
break;
}
}
if (dp[l][r]) continue;
for (int i = l,j = r; i < j - 1; ) {
if (!dp[l][i]) i ++;
else if (!dp[j][r]) j --;
else if (dp[l][i] == dp[j][r]) {
if (dp[i + 1][j - 1]) {
dp[l][r] = dp[l][i] + dp[j][r] + dp[i + 1][j - 1];
break;
}
else i ++,j --;
}
else if (dp[l][i] < dp[j][r]) i ++;
else j --;
}
}
}
int ans = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
ans = max(ans,dp[i][j]);
}
}
fw(ans);
return 0;
}
Day 17
7145 合法序列
写了个暴力,感觉是先预处理出前面的
点击查看代码
int n,k;
lwl w[N];
lwl h[N];
lwl dp[20][N];
lwl ksm(lwl a,lwl k) {
lwl ans = 1;
while (k) {
if (k & 1) ans = ans * a % mod;
a = a * a % mod;
k >>= 1;
}
return ans;
}
int main(){
n = fr(),k = fr();
if (k == 1) {
lwl ans = ksm(2,n - 2);
fw(ans);
return 0;
}
if (k == 2) {
if (n == 4) {
fw(2);
return 0;
}
if (n == 5) {
fw(4);
return 0;
}
h[1] = 1,h[2] = 1;
w[0] = 4;
n -= 5;
for (int i = 1; i <= n; i ++) {
if (i > 2) h[i] = (h[i - 1] + h[i - 2]) % mod;
w[i] = (w[i - 1] * 2 - h[i]) % mod;
}
fw((w[n] % mod + mod) % mod);
return 0;
}
for (int i = 0; i < (1 << k); i ++) {
for (int j = 0; j < k; j ++) {
h[i] |= ((i >> j) & 1) * (1 << (k - j - 1));
}
}
lwl ans = 0;
int W = (1 << (1 << k)) - 1;
int www = (1 << k) - 1;
bool flag;
for (int i = 0; i <= W; i ++) {
flag = true;
for (int j = 0; j <= www - k + 1; j ++) {
int t = (i >> j) & www;
t = h[t];
if (! ((i >> t) & 1)) {
flag = false;
break;
}
}
if (!flag) continue;
memset(dp,0,sizeof dp);
dp[i >> (1 << k) - k][(1 << k) - 1] = 1;
for (int j = 1 << k; j < n; j ++) {
for (int l = 0; l < (1 << k); l ++) {
if ((i & (1 << h[l])) == 0)
continue;
int t = ((l | (1 << k - 1)) ^ (1 << k - 1)) << 1;
dp[l][j] = (dp[t | 1][j - 1] + dp[t][j - 1]) % mod;
}
}
for (int j = 0; j <= www; j ++)
ans = (ans + dp[j][n - 1]) % mod;
}
fw(((ans % mod) + mod) % mod);
return 0;
}
5583 Ethan and Sets
区间
点击查看代码
struct node{
int ml,hate;
bitset<M> t;
};
int n,m,d;
node w[N];
bool flag[M];
bool st[M];
pii dp[N][N];
lwl ml[N],hate[N];
int main(){
n = fr(),m = fr(),d = fr();
int a,b;
for (int i = 1; i <= d; i ++) {
a = fr();
flag[a] = true;
}
for (int i = 1; i <= n; i ++) {
w[i].ml = fr();
b = fr();
while (b --) {
a = fr();
if (flag[a]) w[i].t[a] = 1;
else w[i].hate ++;
}
ml[i] = ml[i - 1] + w[i].ml;
hate[i] = hate[i - 1] + w[i].hate;
}
memset(dp,0x3f,sizeof dp);
bitset<M> truu;
for (int i = 0; i <= m; i ++) {
if (flag[i]) {
truu[i] = 1;
}
}
int l,r;
for (l = 1; l <= n; l ++) {
bitset<M> vis(0);
for (r = l; r <= n; r ++) {
vis |= w[r].t;
if (vis == truu) break;
}
if (vis == truu) {
for (; r <= n; r ++) {
dp[l][r] = {hate[r] - hate[l - 1],ml[r] - ml[l - 1]};
}
}
}
l = 0,r = 0;
for (int i = 1; i <= n; i ++) {
for (int j = i; j <= n; j ++) {
if (dp[i][j].fi == linf) continue;
if (dp[l][r].fi == dp[i][j].fi && dp[l][r].se < dp[i][j].se) {
l = i,r = j;
} else if (dp[l][r].fi > dp[i][j].fi) {
l = i,r = j;
}
}
}
if (l == 0 && r == 0) wj;
else {
fw(l),kg;
fw(r),ch;
}
return 0;
}
Day 18
3694 邦邦的大合唱站队
是一道状压
每一位对应的就是每一支队伍,然后如果这一位是
点击查看代码
int n,m;
int w[N][25];
int siz[25],cnt[25];
int dp[M];
lwl ans = inf;
int get(int l,int r,int type) {
l ++;
return cnt[type] - w[r][type] + w[l - 1][type];
}
int main(){
n = fr(),m = fr();
int a;
for (int i = 1; i <= n; i ++) {
a = fr();
w[i][a] = 1;
cnt[a] ++;
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
w[i][j] += w[i - 1][j];
}
}
int W = (1 << m) - 1;
for (int i = 1; i <= W; i ++) {
int len = 0;
for (int j = 0; j < m; j ++) {
if ((i >> j) & 1)
len += cnt[j + 1];
}
dp[i] = len;
for (int j = 1; j <= m; j ++) {
if ((i >> (j - 1)) & 1) {
int t = i ^ (1 << (j - 1));
dp[i] = min(dp[i],dp[t] + get(len - cnt[j],len,j));
}
}
}
fw(dp[W]);
return 0;
}
3847 调整队形
这是一道区间
从题目中可以知道,他可以随便变化数列,所以就可以知道转移方程式(?),稍微感性理解一下。()
如果当前左区间和右区间的端点相同的话,可以直接转移
如果不同的话,就可以从 dp[l + 1][r - 1] (让人变化衣服颜色)、dp[l + 1][r]&dp[l][r - 1](踢掉、增加,反正是可以凑到的)
然后就可以做了
点击查看代码
int n;
int w[N];
int dp[N][N];
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
for (int len = 2; len <= n; len ++) {
for (int l = 1; l <= n; l ++) {
int r = l + len - 1;
if (r > n) break;
if (w[l] == w[r]) dp[l][r] = dp[l + 1][r - 1];
else dp[l][r] = min(dp[l + 1][r - 1],min(dp[l + 1][r],dp[l][r - 1])) + 1;
}
}
fw(dp[1][n]);
return 0;
}
6492 STEP
线段树,比较板子,
我愿称之为线段树复建第一步(应该暂时也是最后一步()。
点击查看代码
struct node{
int l,r; // 左右端点
int lenl,lenr,len; // 左边连续最长,右边连续最长,最长
int siz; // 总长度
}w[N * 4];
int n,Q;
void push_up(int idx) {
w[idx].siz = w[il].siz + w[ir].siz;
w[idx].l = w[il].l,w[idx].r = w[ir].r;
w[idx].len = max(w[ir].len,w[il].len);
if (w[il].r != w[ir].l)
w[idx].len = max(w[idx].len,w[ir].lenl + w[il].lenr);
w[idx].lenr = w[ir].lenr,w[idx].lenl = w[il].lenl;
if (w[ir].lenr == w[ir].siz && w[il].r != w[ir].l) {
w[idx].lenr = w[ir].lenr + w[il].lenr;
}
if (w[il].lenl == w[il].siz && w[il].r != w[ir].l) {
w[idx].lenl = w[il].lenl + w[ir].lenl;
}
}
void build(int l,int r,int idx) {
if (l == r) {
w[idx] = {1,1,1,1,1,1};
return ;
}
int mid = (l + r) >> 1;
build(l,mid,il);
build(mid + 1,r,ir);
push_up(idx);
}
void modify(int L,int R,int l,int r,int idx) {
if (L >= l && R <= r) {
w[idx].l = 1 - w[idx].l;
w[idx].r = w[idx].l;
return ;
}
int mid = (L + R) >> 1;
if (mid >= l) modify(L,mid,l,r,il);
if (mid < r) modify(mid + 1,R,l,r,ir);
push_up(idx);
}
int main(){
n = fr(),Q = fr();
build(1,n,1);
int x;
while (Q --) {
x = fr();
modify(1,n,x,x,1);
fw(w[1].len);
ch;
}
return 0;
}
6154 游走
期望
点击查看代码
int n,m;
vector<int> e[N];
int rd[N],cd[N];
lwl w[N];
lwl dis[N];
lwl ksm(lwl a,lwl k) {
lwl ans = 1;
while (k) {
if (k & 1) ans = ans * a % mod;
a = a * a % mod;
k >>= 1;
}
return ans;
}
lwl ny(lwl a) {
return ksm(a,mod - 2);
}
int main(){
n = fr(),m = fr();
int a,b;
while (m --) {
a = fr(),b = fr();
e[b].push_back(a);
cd[b] ++,rd[a] ++;
}
queue<int> q;
for (int i = 1; i <= n; i ++) {
if (rd[i] == 0) q.push(i);
w[i] = 1;
}
lwl ans = 0;
while (q.size()) {
auto u = q.front();
q.pop();
for (auto v : e[u]) {
rd[v] --;
dis[v] += w[u];
dis[v] += dis[u];
dis[v] %= mod;
w[v] += w[u];
w[v] %= mod;
if (rd[v] == 0) q.push(v);
}
}
lwl t = 0;
for (int i = 1; i <= n; i ++) {
t = (t + dis[i]) % mod;
ans = (w[i] + ans) % mod;
}
fw(t * ny(ans) % mod);
return 0;
}
4138 挂饰
思路差不多是对的,就是按照那两个维度就那样,然后不要从后往前转移,以后都不要了!!!
然后那个
点击查看代码
int n;
pii w[N];
int dp[N][N];
// 第几个,空余几个挂钩
bool cmp(pii a,pii b) {
if (a.se != b.se) return a.fi > b.fi;
return a.se > b.se;
}
int main(){
n = fr();
int a,b;
int maxn = 0;
for (int i = 1; i <= n; i ++) {
a = fr(),b = fr();
w[i] = {a,b};
maxn = max(maxn,b);
}
sort(w + 1,w + 1 + n,cmp);
memset(dp,-0x3f,sizeof dp);
for (int i = 0; i <= n; i ++) {
dp[0][i] = -inf;
dp[i][n + 1] = -inf;
}
dp[0][1] = 0;
lwl ans = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= n; j ++) {
dp[i][j] = max(dp[i - 1][j],dp[i - 1][max(j - w[i].fi,0) + 1] + w[i].se);
}
}
for (int i = 0; i <= n; i ++) {
ans = max(ans,dp[n][i]);
}
fw(ans);
return 0;
}
4095 Eden的新背包问题
在做这个题的时候一开始写的是
暴力就不多说了,就是每一个去掉的都枚举一遍。然后正解的话是先把多重背包转化成01背包,然后再从前往后跑一次背包,从后往前跑一次背包。在后面每一次询问的时候再遍历一下是多少放在前面用,多少放在后面用加起来再取最大值就可以了。然后还有没有用完的情况,所以加了一小段。
点击查看代码
int n,Q;
lwl dp[N][N];
lwl f[N][N];
lwl ans[N][N];
vector<pii> w[N];
int main(){
n = fr();
int a,b,c;
for (int i = 1; i <= n; i ++) {
a = fr(),b = fr(),c = fr();
for (int k = 1; k <= c; k ++) {
c -= k;
w[i].push_back({k * a,k * b});
}
if (c) w[i].push_back({c * a,c * b});
}
memset(dp,-0x3f,sizeof dp);
memset(f,-0x3f,sizeof f);
for (int i = 0; i <= n + 1; i ++) {
dp[i][0] = 0;
f[i][0] = 0;
}
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= 1000; j ++)
dp[i][j] = dp[i - 1][j];
for (auto it : w[i]) {
int ne = it.fi,val = it.se;
for (int j = 1000; j >= ne; j --) {
dp[i][j] = max(dp[i][j],dp[i][j - ne] + val);
}
}
}
for (int i = n; i >= 1; i --) {
for (int j = 0; j <= 1000; j ++)
f[i][j] = f[i + 1][j];
for (auto it : w[i]) {
int ne = it.fi,val = it.se;
for (int j = 1000; j >= ne; j --) {
f[i][j] = max(f[i][j],f[i][j - ne] + val);
}
}
}
int max1 = 0,max2 = 0;
for (int i = 1; i <= n; i ++) {
max1 = 0,max2 = 0;
for (int j = 0; j <= 1000; j ++) {
max1 = max(dp[i][j],max1);
dp[i][j] = max1;
max2 = max(f[i][j],max2);
f[i][j] = max2;
}
}
int d,e;
Q = fr();
while (Q --) {
lwl ans = 0;
d = fr(),e = fr();
d ++;
for (int i = 0; i <= e; i ++) {
ans = max(ans,dp[d - 1][i] + f[d + 1][e - i]);
}
fw(ans);
ch;
}
return 0;
}
Day 19
1301 魔鬼之城
是一道
然后发现自己好像写建边的时候容易把
点击查看代码
int n,m;
bool flag[N * N][8];
int w[N][N];
int dx[8] = {1,1,-1,-1,1,-1,0,0};
int dy[8] = {1,-1,1,-1,0,0,1,-1};
vector<pii> e[N * N];
int dis[N * N][9];
int get(int x,int y) {
return (x - 1) * m + y;
}
pii zb(int w) {
int t = w % m;
if (!t) {
t = m;
w -= m;
}
return {(w / m) + 1,t};
}
int bfs() {
queue<pii> q;
q.push({get(1,1),8});
for (int i = 0; i < 8; i ++) {
flag[get(1,1)][i] = true;
dis[get(1,1)][i] = 0;
}
dis[get(1,1)][8] = 0;
while (q.size()) {
auto t = q.front();
q.pop();
int type = t.se,u = t.fi;
int x = zb(u).fi,y = zb(u).se;
for (auto it : e[u]) {
if (it.se == type) continue;
int v = it.fi;
if (flag[v][it.se]) continue;
flag[v][it.se] = true;
if (v == get(n,m))
return dis[u][type] + 1;
dis[v][it.se] = dis[u][type] + 1;
q.push({v,it.se});
}
}
return inf;
}
int main(){
m = fr(),n = fr();
int x,y;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
w[i][j] = fr();
for (int k = 0; k < 8; k ++) {
x = i + dx[k] * w[i][j],y = j + dy[k] * w[i][j];
if (x > n || y > m || x <= 0 || y <= 0)
continue;
e[get(i,j)].push_back({get(x,y),k});
}
}
}
int ans = bfs();
if (ans == inf) wj;
else fw(ans);
return 0;
}
4303 基因匹配
一开始做的时候觉得是最长公共子序列加一些啥优化,也尝试了一些玄学优化,但是很明显失败了,而且比之前那个朴素的还要慢一些,反 向 优 化
正解:树状数组和
点击查看代码
int n,m;
int w[N],h[N];
int dp[N],tr[N];
vector<int> y[N];
void update(int pos,int x) {
while (pos <= m) {
tr[pos] = max(tr[pos],x);
pos += (pos & -pos);
}
}
int query(int pos) {
int ans = 0;
while (pos) {
ans = max(ans,tr[pos]);
pos -= (pos & -pos);
}
return ans;
}
int main(){
n = fr();
m = 5 * n;
for (int i = 1; i <= 5 * n; i ++) {
w[i] = fr();
}
for (int j = 1; j <= 5 * n; j ++) {
h[j] = fr();
y[h[j]].push_back(j);
}
for (int i = 1; i <= m; i ++) {
for (int j = y[w[i]].size() - 1; j >= 0; j --) {
int pos = y[w[i]][j];
dp[pos] = query(pos - 1) + 1;
update(pos,dp[pos]);
}
}
int ans = 0;
for (int i = 1; i <= m; i ++) {
ans = max(ans,dp[i]);
}
fw(ans);
return 0;
}
5664 Emiya家今天的饭
断网做的时候完全搞不捣吧,然后
正解是容斥 +
然后不合法的方案数就是这一列选了超过一半的情况,也就是
然后考虑所有的状态,和前面的设计类似,
最后再一减就可以得到答案捏。
点击查看代码
int n,m;
int w[N][M],sum[N];
lwl dp[N][N * 2],f[N][N];
int main(){
n = fr(),m = fr();
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
w[i][j] = fr();
sum[i] = (sum[i] + w[i][j]) % mod;
}
}
lwl ans = 0;
// 计算非法方案数
for (int col = 1; col <= m; col ++) {
memset(dp,0,sizeof dp);
dp[0][n] = 1;
for (int i = 1; i <= n; i ++) {
for (int j = n - i; j <= n + i; j ++) {
dp[i][j] = (dp[i - 1][j] + dp[i - 1][max(0,j - 1)] * w[i][col] % mod + dp[i - 1][j + 1] * (sum[i] - w[i][col]) % mod) % mod;
}
}
for (int i = 1; i <= n; i ++)
ans = (ans + dp[n][n + i]) % mod;
}
// 计算合法方案数
f[0][0] = 1;
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= n; j ++) {
int b;
if (!j) b = 0;
else b = 1;
f[i][j] = (f[i - 1][j] + b * f[i - 1][max(0,j - 1)] * sum[i] % mod) % mod;
}
}
lwl summ = 0;
for (int i = 1; i <= n; i ++) {
summ = (summ + f[n][i]) % mod;
}
ans = ((summ - ans) % mod + mod) % mod;
fw(ans);
return 0;
}
6801 花式围栏
单调栈。考虑每一个高度分开算。一开始断网的时候调了一个小时调不出来,简直脑袋要炸了
点击查看代码
struct node{
int h,w;
};
int n;
node a[N];
lwl y(lwl x) {
return x * (x + 1) / 2 % mod;
}
int main(){
n = fr();
lwl w,h;
for (int i = 1; i <= n; i ++) {
a[i].h = fr();
}
for (int i = 1; i <= n; i ++) {
a[i].w = fr();
}
stack<lwl> s;
stack<lwl> len;
lwl length = 0;
lwl ans = 0;
a[n + 1].h = 0;
s.push(-1);
for (int i = 1; i <= n + 1; i ++,length = 0) {
while (a[i].h <= s.top()) {
h = s.top();
w = len.top();
s.pop(),len.pop();
length = (length + w) % mod;
ans = ans + (y(h) - y(max(a[i].h,s.top()))) * y(length) % mod;
}
s.push(a[i].h);
len.push((length + a[i].w) % mod);
}
fw((ans % mod + mod) % mod);
return 0;
}
5459 回转寿司
本来看题解应该是一道线段树或者分治的题,但是学了
题目说要求这个区间的和在区间
然后就是
点击查看代码
lwl n,L,R;
lwl w[N];
lwl sum[N];
rope<lwl> h;
int main(){
n = fr(),L = fr(),R = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
sum[i] = sum[i - 1] + w[i];
}
lwl t = 0;
lwl ans = 0;
lwl l,r,ll,rr;
h.insert(0);
for (int i = 1; i <= n; i ++) {
t = sum[i];
l = t - R,r = t - L;
rr = upper_bound(h.begin(),h.end(),r) - h.begin();
ll = lower_bound(h.begin(),h.end(),l) - h.begin();
ans += rr - ll;
h.insert(lower_bound(h.begin(),h.end(),t) - h.begin(),t);
}
fw(ans);
ch;
return 0;
}
4019 多边形染色
这题断网做的时候比花式围栏还折磨,调了一个小时捏妈,人都快给我调升天了。还好代码调出来基本就是对的了,就是一开始在做的时候像个勺一样在那里枚举最后一个点的颜色然后从一号点开始遍历,枚举一号点的颜色不是更简单吗!!!!
然后这个题一开始一直开
点击查看代码
struct node{
int type,x,p;
}q[1005];
int n,m,c;
int col[N];
int flag[N][15];
lwl dp[N][15];
int id[N];
int h[N];
int find(int x){
if (x != h[x]) h[x] = find(h[x]);
return h[x];
}
int main(){
n = fr(),m = fr(),c = fr();
for (int i = 1; i <= n; i ++) {
h[i] = i;
}
int type,x,p;
int cnt = 0;
while (m --) {
type = fr(),x = fr(),p = fr();
if (type == 3) {
h[find(x)] = h[find(p)];
}
else q[++ cnt] = {type,x,p};
}
int idx = cnt;
cnt = 0;
for (int i = 1; i <= n; i ++) {
if (h[i] != i) continue;
id[i] = ++ cnt;
for (int j = 1; j <= c; j ++) {
flag[cnt][j] = 1;
}
}
for (int i = 1; i <= idx; i ++) {
type = q[i].type,x = q[i].x,p = q[i].p;
if (type == 1) {
for (int j = 1; j <= c; j ++) {
if (j == p) continue;
flag[id[find(x)]][j] = 0;
}
}
else flag[id[find(x)]][p] = 0;
}
lwl ans = 0;
for (int t = 1; t <= c; t ++) {
if (! flag[id[find(1)]]) continue;
memset(dp,0,sizeof dp);
dp[1][t] = 1;
for (int tt = 2; tt <= cnt; tt ++) {
for (int j = 1; j <= c; j ++) {
if (!flag[tt][j]) continue;
for (int k = 1; k <= c; k ++) {
if (k == j) continue;
dp[tt][j] = (dp[tt][j] + dp[tt - 1][k]) % mod;
}
}
}
for (int j = 1; j <= c; j ++) {
if (j == t) continue;
ans = (ans + dp[cnt][j]) % mod;
}
}
fw(ans);
return 0;
}
Day 20
4124 手机号码
数位
点击查看代码
lwl L,R;
int dp[N][N][N][2][2][2];
vector<int> a;
// 位数,前一、二位的数,有无四,有无八,是否限制,有无连着的三位数
lwl dfs(int w,int u1,int u2,bool si,bool ba,bool limit,bool yeah) {
if (si && ba) return 0;
if (!w && yeah) return 1;
else if (!w) return 0;
if (!limit && ~dp[w][u1][u2][yeah][si][ba])
return dp[w][u1][u2][yeah][si][ba];
lwl ans = 0;
int up;
if (limit) up = a[w];
else up = 9;
for (int i = 0; i <= up; i ++) {
if (w == 11 && i == 0) continue;
if ((si && i == 8) || (ba && i == 4)) continue;
bool a = si | (i == 4),b = ba | (i == 8);
bool c = limit & (i == up);
if (u1 == u2 && u1 == i)
ans += dfs(w - 1,u2,i,a,b,c,1);
else ans += dfs(w - 1,u2,i,a,b,c,yeah);
}
if (limit) return ans;
return dp[w][u1][u2][yeah][si][ba] = ans;
}
lwl wdwx(lwl x) {
if (x == 1e10 - 1) return 0;
a.clear();
a.push_back(0);
while (x) {
a.push_back(x % 10);
x /= 10;
}
return dfs(11,-2,10,0,0,1,0);
}
int main(){
L = fr(),R = fr();
memset(dp,-1,sizeof dp);
lwl ans = wdwx(R) - wdwx(L - 1);
fw(ans);
return 0;
}
4160 生日快乐
正解就比较暴力()。但是因为写正解写完了还没有发现
点击查看代码
int n;
int x,y;
double S;
// 这一块要分成 u 块,这一块的两条边
double dfs(int u,double x,double y) {
if (u == 1) {
if (x < y) swap(x,y);
return x / y;
}
double ans = inf;
for (int i = 1; i <= u / 2; i ++) {
double ma1 = max(dfs(i,x * i / u,y),dfs(u - i,x * (u - i) / u,y));
double ma2 = max(dfs(i,x,y * i / u),dfs(u - i,x,y * (u - i) / u));
ans = min(ans,min(ma1,ma2));
}
return ans;
}
int main(){
x = fr(),y = fr(),n = fr();
S = x * y * 1.0 / n;
double ans = dfs(n,1.0 * x,1.0 * y);
printf("%.6lf",ans);
return 0;
}
6767 Roses
本来可以一遍过掉的,但是没开
捆绑测试一生之敌!!!!!!!!!(捆绑测试爆零一家人整整齐齐())
点击查看代码
lwl n;
lwl a,b,c,d;
int main(){
lwl ans;
n = fr();
a = fr(),b = fr(),c = fr(),d = fr();
if (b * c > a * d) {
swap(a,c);
swap(b,d);
}
ans = n / a * b;
if (n % a == 0) {
fw(ans);
return 0;
}
else {
ans = linf;
for (int i = 0; i <= a; i ++) {
if(n <= c * i)
ans = min(ans, d * i);
else {
lwl sy = n - c * i;
if (sy % a == 0) sy = sy / a;
else sy = sy / a + 1;
ans = min(ans, d * i + sy * b);
}
}
}
fw(ans);
return 0;
}
Taming the Hard G
然后断网的时候写的也是错误的贪心,只有
点击查看代码
int n;
int w[N];
int dp[N][N];
// 前 i 天逃了 n 次的最小次数
int cnt[N][N];
// 第 i 天出逃到第 j 天需要修改的次数(最近一次出逃)
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
for (int i = 0; i <= n; i ++) {
int t = 0;
for (int j = i; j <= n; j ++) {
if (w[j] != j - i)
t ++;
cnt[i][j] = t;
}
}
memset(dp,0x3f,sizeof dp);
dp[0][0] = 0;
for (int i = 0; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
for (int k = i + 1; k <= n; k ++)
dp[k][j] = min(dp[k][j],dp[i][j - 1] + cnt[i + 1][k]);
}
}
for (int i = 1; i <= n; i ++) {
fw(dp[n][i]);
ch;
}
return 0;
}
6835 线性生物
期望
本来前天做那个游走的时候感觉期望
期望的线性性质:
在这题的体现 :
对于这类在图上随机游走的问题,我们一般会设
将
此时令
发现式子两边都含有
然后按照这个式子转移,因为是
点击查看代码
int n,m,id,maxn;
int d[N];
lwl w[N],sum[N];
vector<int> e[N];
int main(){
id = fr();
n = fr(),m = fr();
if (id == 1) {
fw(n * 2);
return 0;
}
for (int i = 1; i <= n; i ++) {
d[i] ++;
}
int a,b;
while (m --) {
a = fr(),b = fr();
e[a].push_back(b);
d[a] ++;
}
for (int x = 1; x <= n; x ++) {
w[x] = d[x];
for (auto y : e[x]) {
w[x] = (w[x] + (sum[x - 1] - sum[y - 1]) % mod) % mod;
}
sum[x] = (sum[x - 1] + w[x]) % mod;
}
fw((sum[n] % mod + mod) % mod);
return 0;
}
Day 21
7162 Sjekira
本来可以过的,但是中间那个判断这条边走没走过的时候,一开始脑抽写错了,写的是 :
点击查看代码
int n;
int w[N];
int id[N];
pii sx[N];
bool flag[N];
int h[N];
vector<int> e[N];
bool cmp(int a,int b) {
return w[a] > w[b];
}
int find(int x) {
if (h[x] != x) h[x] = find(h[x]);
return h[x];
}
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
id[i] = i;
}
sort(id + 1,id + 1 + n,cmp);
int a,b;
for (int i = 1; i < n; i ++) {
a = fr(),b = fr();
e[a].push_back(b);
e[b].push_back(a);
}
int idx = 0;
for (int i = 1; i <= n; i ++) {
int u = id[i];
flag[u] = true;
for (auto v : e[u]) {
if(flag[v]) continue;
sx[++ idx] = {u,v};
}
}
for (int i = 1; i <= n; i ++) {
h[i] = i;
}
lwl ans = 0;
for (int i = idx; i; i --) {
int u = sx[i].fi,v = sx[i].se;
ans += w[find(u)] + w[find(v)];
if (w[find(u)] < w[find(v)]) h[find(u)] = find(v);
else h[find(v)] = find(u);
}
fw(ans);
return 0;
}
7149 Rectangular Pasture S
一开始写的完全错误吧(),但是感觉好像还好的样子,也不知道哪里错了,算了,不管他
正解:题解里面有好多用二维前缀和的,但是看不懂,所以就看的另一种方法 :先按照
点击查看代码
int n;
pii w[N];
lwl a[N],b[N];
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i].fi = fr();
w[i].se = fr();
}
sort(w + 1,w + 1 + n);
lwl ans = 1;
for (int i = 1; i <= n; i ++) {
ans ++;
lwl l = 0,r = 0;
for (int j = i - 1; j > 0; j --) {
if (w[i].se > w[j].se) {
ans += (r + 1) * (a[j] + 1);
b[j] ++;
l ++;
} else {
ans += (l + 1) * (b[j] + 1);
a[j] ++;
r ++;
}
}
}
fw(ans);
return 0;
}
7100 团
也算是一遍过捏,就是一开始有地方忘记开
然后建边小技巧 :把每个集合搞一个点,然后都连到那个点上。
点击查看代码
int n,k;
vector<node> e[N];
pii w[N];
lwl dis[N];
bool flag[N];
void dij() {
memset(dis,0x3f,sizeof dis);
dis[1] = 0;
priority_queue<pii,vector<pii>,greater<pii> > q;
q.push({0,1});
while (q.size()) {
auto t = q.top();
q.pop();
int u = t.se;
lwl dist = t.fi;
if (flag[u]) continue;
for (auto it : e[u]) {
int v = it.v,w = it.w;
if (dis[v] > dist + w) {
dis[v] = dist + w;
q.push({dis[v],v});
}
}
flag[u] = true;
}
}
int main(){
n = fr(),k = fr();
int m;
int cnt = 0;
while (k --) {
m = fr();
cnt ++;
for (int i = 1; i <= m; i ++) {
w[i].fi = fr(),w[i].se = fr();
e[n + cnt].push_back({w[i].fi,w[i].se});
e[w[i].fi].push_back({n + cnt,w[i].se});
}
}
dij();
for (int i = 1; i <= n; i ++) {
if (dis[i] >= linf / 2) fw(linf);
else fw(dis[i]);
kg;
}
return 0;
}
6503 DIFERENCIJA
一遍过捏。
点击查看代码
int n;
lwl w[N];
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
stack<lwl> maxn,minn;
stack<lwl> len1,len2;
len1.push(0),len2.push(0);
lwl ans = 0;
for (int i = 1; i <= n + 1; i ++) {
while (maxn.size() && (maxn.top() < w[i] || i == n + 1)) {
auto t = maxn.top();
maxn.pop();
auto pos = len1.top();
len1.pop();
int len = pos - len1.top();
ans += (lwl)len * (i - pos) * t;
}
while (minn.size() && (minn.top() > w[i] || i == n + 1)) {
auto t = minn.top();
minn.pop();
auto pos = len2.top();
len2.pop();
int len = pos - len2.top();
ans -= (lwl)len * (i - pos) * t;
}
minn.push(w[i]);
len1.push(i);
maxn.push(w[i]);
len2.push(i);
}
fw(ans);
return 0;
}
Day 22
8127 The Xana coup
代码比较清楚捏,但是考试的时候应该写不出来()。链的可以搞一搞。
点击查看代码
int n;
int w[N];
vector<int> e[N];
int dp[N][4]; // inf => 不合法
// 0 => 权值变为 0 , 没有对节点操作 时对应的最小操作次数
// 1 => 权值变为 1 , 没有对节点操作 时对应的最小操作次数
// 2 => 权值变为 0 , 对节点进行了操作 对应的最小操作次数
// 3 => 权值变为 1 , 对节点进行了操作 对应的最小操作次数
int ou[N][2],ji[N][2]; // inf => 不合法
// 这里的操作数对应的都是子节点的操作数
// ou0 => u节点的前 i 节点全部都是 0 且有 偶数 个操作操作过
// ou1 => u节点的前 i 节点全部都是 1 且有 偶数 个操作操作过
// ji0 => u节点的前 i 节点全部都是 0 且有 奇数 个操作操作过
// ji1 => u节点的前 i 节点全部都是 1 且有 奇数 个操作操作过
void dfs(int u,int fa) {
if (e[u].size() == 1 && fa) { // 叶子节点
if (w[u] == 0) {
dp[u][0] = 0;
dp[u][1] = inf;
dp[u][2] = inf;
dp[u][3] = 1;
} else {
dp[u][0] = inf;
dp[u][1] = 0;
dp[u][2] = 1;
dp[u][3] = inf;
}
return ;
}
for (auto v : e[u]) {
if (v == fa) continue;
dfs(v,u);
}
int i = 0;
ou[0][0] = ou[0][1] = 0;
ji[0][0] = ji[0][1] = inf;
for (auto v : e[u]) {
if (v == fa) continue;
i ++;
ou[i][0] = min(ou[i - 1][0] + dp[v][0],ji[i - 1][0] + dp[v][2]);
ou[i][1] = min(ou[i - 1][1] + dp[v][1],ji[i - 1][1] + dp[v][3]);
ji[i][0] = min(ji[i - 1][0] + dp[v][0],ou[i - 1][0] + dp[v][2]);
ji[i][1] = min(ji[i - 1][1] + dp[v][1],ou[i - 1][1] + dp[v][3]);
}
if (w[u] == 0) {
dp[u][0] = ou[i][0];
dp[u][1] = ji[i][0];
dp[u][2] = 1 + ji[i][1];
dp[u][3] = 1 + ou[i][1];
} else {
dp[u][0] = ji[i][0];
dp[u][1] = ou[i][0];
dp[u][2] = 1 + ou[i][1];
dp[u][3] = 1 + ji[i][1];
}
}
int main(){
n = fr();
int a,b;
bool flag = true;
for (int i = 1; i < n; i ++) {
a = fr(),b = fr();
if (abs(a - b) != 1) flag = false;
e[a].push_back(b);
e[b].push_back(a);
}
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
if (flag) {
lwl ans = 0;
for (int i = 1; i < n; i ++) {
if (w[i]) {
ans ++;
w[i + 1] = 1 - w[i + 1];
w[i + 2] = 1 - w[i + 2];
}
}
if (w[n]) wj;
else fw(ans);
return 0;
}
dfs(1,0);
lwl ans = min(dp[1][0],dp[1][2]);
if (ans >= inf) wj;
else fw(ans);
return 0;
}
Day 23
7299 Dance Mooves S
因为这个是可以不断循环的,所以说如果
然后要注意的就是
点击查看代码
int n,k;
pii w[M];
int a[N],h[N];
vector<int> e[N];
set<int> ans[N];
int find(int x) {
if (x != h[x]) h[x] = find(h[x]);
return h[x];
}
int main(){
n = fr(),k = fr();
for (int i = 1; i <= n; i ++) {
a[i] = i;
h[i] = i;
e[i].push_back(i);
}
for (int i = 1; i <= k; i ++) {
w[i].fi = fr();
w[i].se = fr();
}
for (int i = 1; i <= k; i ++) {
swap(a[w[i].fi],a[w[i].se]);
e[a[w[i].fi]].push_back(w[i].fi);
e[a[w[i].se]].push_back(w[i].se);
}
for (int i = 1; i <= n; i ++) {
int ha = find(i),hb = find(a[i]);
h[hb] = ha;
}
for (int i = 1; i <= n; i ++) {
int u = find(i);
for (auto v : e[i]) {
ans[u].insert(v);
}
}
for (int i = 1; i <= n; i ++) {
fw(ans[find(i)].size());
ch;
}
return 0;
}
9017 Lights Off G
首先在一轮操作中,只有第一个操作需要我们选择, 所以我们把第一个操作和后面两个操作分开算。
然后其实每一次操作的贡献只和一共进行了多少步有关。假设一共进行了
考虑状压
根据题解所说:如果
最后输入
点击查看代码
int n,T;
int dp[LEN][M]; // j 通过 i 步能不能到达 0
int w[M];
int a,b;
int move(int x) {
return (x >> 1) + ((x & 1) << (n - 1));
}
int main(){
T = fr(),n = fr();
int maxn = n * 2;
int m = (1 << n) - 1;
// 预处理
memset(w,-1,sizeof w);
for (int i = 0; i <= m; i ++) {
int x = i;
// 可以循环到达的点
while (w[x] == -1) {
w[x] = i;
x = move(x);
}
}
dp[0][0] = 1;
int u = 0;
for (int i = 1; i <= maxn; i ++) {
u ^= 1 << ((i - 1) % n);
for (int j = 0; j <= m; j ++) {
dp[i][w[j]] |= dp[i - 1][w[j ^ u]];
}
}
while (T --) {
string s;
cin >> s;
a = 0,b = 0; // 多组数据
bool flag = true;
for (int i = 0; i < n; i ++) {
a |= (s[i] - '0') * (1 << (n - i - 1));
if (s[i] == '1') flag = false;
}
cin >> s;
if (flag) {
wj;
continue;
}
for (int i = 0; i < n; i ++) {
b |= (s[i] - '0') * (1 << (n - i - 1));
}
for (int i = 1; ; i ++) {
a ^= b; // 模拟操作 2
if (dp[i][w[a]]) {
fw(i);
ch;
break;
}
b = move(b); // 模拟操作 3
}
}
return 0;
}
Day 24
7300 No Time to Paint S
因为题目范围,所以想到每一次询问都在线询问不可取,于是预处理。
因为这题的区间中间一段是断开的,所以想到预处理前后缀。前后缀代表的就是从
然后如果遍历到这个
最后询问的时候把前缀和后缀拼在一起就可以了。
点击查看代码
int n,Q;
int dp[N],f[N];
int w[N];
bool flag[N];
int main(){
n = fr(),Q = fr();
string s;
cin >> s;
for (int i = 0; i < n; i ++) {
w[i + 1] = s[i] - 'A' + 1;
}
memset(flag,0,sizeof flag);
for (int i = 1; i <= n; i ++) {
if (flag[w[i]]) dp[i] = dp[i - 1];
else dp[i] = dp[i - 1] + 1;
flag[w[i]] = true;
for (int k = w[i] + 1; k <= 26; k ++) {
flag[k] = false;
}
}
memset(flag,0,sizeof flag);
for (int i = n; i; i --) {
if (flag[w[i]]) f[i] = f[i + 1];
else f[i] = f[i + 1] + 1;
flag[w[i]] = true;
for (int k = w[i] + 1; k <= 26; k ++) {
flag[k] = false;
}
}
int l,r;
while (Q --) {
l = fr(),r = fr();
fw(dp[l - 1] + f[r + 1]);
ch;
}
return 0;
}
7284 Patkice II
也算是一遍过吧,就是一开始开数组的时候没有用
然后这题需要
点击查看代码
int n,m,st,en;
char c[N][N];
int dis[N * N];
bool flag[N * N];
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
vector<node> e[N * N];
int from[N * N];
int get(int x,int y) {
return (x - 1) * m + y;
}
pii zb(int w) {
int t = w % m;
if (!t) {
t = m;
w -= m;
}
return {(w / m) + 1,t};
}
void spfa() {
deque<int> q;
q.push_front(st);
memset(dis,0x3f,sizeof dis);
dis[st] = 0;
while (q.size()) {
auto u = q.front();
q.pop_front();
flag[u] = false;
for (auto it : e[u]) {
int v = it.v;
if (dis[v] > dis[u] + it.w) {
dis[v] = dis[u] + it.w;
from[v] = u;
if (!flag[v]) {
flag[v] = true;
if (q.size() && dis[v] < dis[q.front()])
q.push_front(v);
else q.push_back(v);
}
}
}
}
}
int main(){
n = fr(),m = fr();
string s;
int w,f;
for (int i = 1; i <= n; i ++) {
cin >> s;
for (int j = 0; j < m; j ++) {
c[i][j + 1] = s[j];
w = inf;
if (s[j] == '>') w = 0;
else if (s[j] == '<') w = 1;
else if (s[j] == '^') w = 2;
else if (s[j] == 'v') w = 3;
else if (s[j] == 'o') st = get(i,j + 1);
else if (s[j] == '.') w = 5;
else en = get(i,j + 1);
if (w == inf) f = 0;
else f = 1;
int u = get(i,j + 1);
for (int k = 0; k < 4; k ++) {
if (i + dx[k] > n || i + dx[k] <= 0) continue;
if (j + 1 + dy[k] > m || j + 1 + dy[k] <= 0) continue;
int v = get(i + dx[k],j + 1 + dy[k]);
if (w == k) e[u].push_back({v,f - 1});
else e[u].push_back({v,f});
}
}
}
spfa();
fw(dis[en]);
ch;
while (from[en] != st) {
if (en == from[en] + m)
c[zb(from[en]).fi][zb(from[en]).se] = 'v';
else if (en == from[en] - m)
c[zb(from[en]).fi][zb(from[en]).se] = '^';
else if (en == from[en] + 1)
c[zb(from[en]).fi][zb(from[en]).se] = '>';
else if (en == from[en] - 1)
c[zb(from[en]).fi][zb(from[en]).se] = '<';
en = from[en];
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
cout << c[i][j];
}
ch;
}
return 0;
}
6812 Ancestor 先辈
线段树。
显而易见,要满足这个区间是一个先辈,只要他是一个单调不减的序列就可以,此时联想到差分,就维护序列的差分值,然后线段树里面是每一段区间的最小值,只要
注意 :
点击查看代码
struct node{
lwl minn;
}w[N * 4];
int n,k;
int a[N];
void pushup(int idx) {
w[idx].minn = min(w[ir].minn,w[il].minn);
}
void build(int l,int r,int idx) {
if (l == r) {
w[idx].minn = a[l];
return ;
}
int mid = (l + r) >> 1;
if (mid >= l) build(l,mid,il);
if (mid < r) build(mid + 1,r,ir);
pushup(idx);
}
void modify(int L,int R,int l,int r,int idx,int x) {
if (L >= l && R <= r) {
w[idx].minn += x;
return ;
}
int mid = (L + R) >> 1;
if (mid >= l) modify(L,mid,l,r,il,x);
if (mid < r) modify(mid + 1,R,l,r,ir,x);
pushup(idx);
}
lwl qurey(int L,int R,int l,int r,int idx) {
if (L >= l && R <= r) {
return w[idx].minn;
}
int mid = (L + R) >> 1;
lwl minx = linf;
if (mid >= l) minx = min(minx,qurey(L,mid,l,r,il));
if (mid < r) minx = min(minx,qurey(mid + 1,R,l,r,ir));
return minx;
}
int main(){
n = fr(),k = fr();
for (int i = 1; i <= n; i ++) {
a[i] = fr();
}
for (int i = n; i; i --)
a[i] = a[i] - a[i - 1];
build(1,n,1);
int type,l,r,x;
while (k --) {
type = fr();
if (type == 1) {
l = fr(),r = fr(),x = fr();
modify(1,n,l,l,1,x);
modify(1,n,r + 1,r + 1,1,-x);
} else {
l = fr(),r = fr();
if (r == n + 1) r = n;
lwl ans = qurey(1,n,l + 1,r,1);
if (ans < 0) wj;
else yj;
}
}
return 0;
}
7716 Covering
对于每一个
-
如果在已知图中出现了一次
,那么这个 有可能在四个方向。但是如果该方向的数字比 要小的话,那这个方向就是不合法的(因为如果在这个方向会覆盖掉)。四个方向全部跑一次,记录下合法的方案,再和 相乘就是 -
如果在已知图中出现了两次
,那么就说明 的位置是固定的,只能在这个位置,又因为当前纸片必须选,所以直接 就可以了。 -
如果在已知图中没有出现过
,那就说明 这个编号的纸片可选可不选。不选的话就是直接 ,选的话就需要统计当前纸片可以被完全覆盖住的地方,这里用一个前缀和来统计。因为大的点是可以覆盖小的点,所以是从后往前前缀和。
点击查看代码
int n,m,k;
int l,r;
int w[N][N];
int sum[N];
lwl dp[N][N],sl[N];
pii zb[N];
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
int get(int x,int y) {
return (x - 1) * m + y;
}
int main(){
int T = fr();
while (T --) {
n = fr(),m = fr(),k = fr(),l = fr(),r = fr();
memset(w,0,sizeof w);
memset(sum,0,sizeof sum);
memset(sl,0,sizeof sl);
int maxn = -inf,cnt = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
w[i][j] = fr();
if (!w[i][j]) continue;
maxn = max(w[i][j],maxn);
sl[w[i][j]] ++;
zb[w[i][j]] = {i,j};
if (i > 1 && w[i - 1][j])
sum[min(w[i - 1][j],w[i][j])] ++;
if (j > 1 && w[i][j - 1])
sum[min(w[i][j - 1],w[i][j])] ++;
}
}
for (int i = maxn; i; i --)
sum[i] = (sum[i + 1] + sum[i]) % mod;
dp[0][0] = 1;
for (int i = 1; i <= maxn; i ++) {
dp[i][cnt] = dp[i - 1][cnt];
if (sl[i] == 1) {
int x = zb[i].fi,y = zb[i].se;
lwl num = 0;
for (int k = 0; k < 4; k ++){
if (w[x + dx[k]][y + dy[k]] > i)
num ++;
}
for (int j = cnt + 1; j <= r; j ++)
dp[i][j] = num * dp[i - 1][j - 1] % mod;
cnt ++;
} else if (sl[i] == 2) {
for (int j = cnt + 1; j <= r; j ++)
dp[i][j] = dp[i - 1][j - 1];
cnt ++;
} else {
for (int j = cnt + 1; j <= r; j ++) {
dp[i][j] = (lwl)sum[i] * dp[i - 1][j - 1] % mod;
dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod;
}
}
}
lwl ans = 0;
for (int i = max(l,cnt); i <= r; i ++) {
ans = (ans + dp[maxn][i]) % mod;
}
fw(ans);
ch;
}
return 0;
}
Day 25
7176 Fountain
一遍过捏,一点倍增(?)还有一点单调栈。
点击查看代码
int n,Q;
pii w[N];
vector<node> e[N];
int dis[N],de[N];
int fa[N][20];
void dfs(int u,int father,int t) {
if (~father) {
de[u] = de[father] + 1;
dis[u] = dis[father] + t;
fa[u][0] = father;
}
for(int k = 1; k <= log2(de[u]); k ++)
fa[u][k] = fa[fa[u][k - 1]][k - 1];
for (auto it : e[u]) {
dfs(it.v,u,it.w);
}
}
int find(int u,int vv) {
for (int k = log2(de[u]); k >= 0; k --) {
if (dis[u] - dis[fa[u][k]] < vv) {
vv -= dis[u] - dis[fa[u][k]];
u = fa[u][k];
}
}
return u;
}
int main(){
n = fr(),Q = fr();
w[0].fi = inf;
for (int i = 1; i <= n; i ++) {
w[i].fi = fr(),w[i].se = fr();
}
stack<int> s;
s.push(0);
for (int i = n; i; i --) {
while (s.size() && w[s.top()].fi <= w[i].fi) s.pop();
e[s.top()].push_back({i,w[i].se});
s.push(i);
}
dfs(0,-1,0);
int u,V;
while (Q --) {
u = fr(),V = fr();
int ans = find(u,V);
fw(ans);
ch;
}
return 0;
}
6512 Quark and Flying Pigs
最短路 +
点击查看代码
int n,m,k;
int dis[N][N];
pii w[5005];
int dp[5005];
int main(){
n = fr(),m = fr(),k = fr();
int a,b,ww;
memset(dis,0x3f,sizeof dis);
for (int i = 1; i <= n; i ++) {
dis[i][i] = 0;
}
while (m --) {
a = fr(),b = fr(),ww = fr();
dis[a][b] = min(dis[a][b],ww);
dis[b][a] = dis[a][b];
}
for (int i = 1; i <= k; i ++) {
w[i].fi = fr();
w[i].se = fr();
}
w[0] = {0,1};
sort(w + 1,w + 1 + k);
for (int k = 1; k <= n; k ++) {
for (int j = 1; j <= n; j ++) {
if (k == j) continue;
for (int i = 1; i <= n; i ++) {
if (j == i || k == i) continue;
dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
}
}
}
dp[0] = 0;
for (int i = 1; i <= k; i ++) {
for (int j = 0; j < i; j ++) {
if (w[i].fi - w[j].fi >= dis[w[i].se][w[j].se])
dp[i] = max(dp[i],dp[j] + 1);
}
}
int ans = 0;
ans = dp[k];
fw(ans);
return 0;
}
6627 幸运数字
暴力的话就是把
正解:根据上面的继续往后推,把这些要对于这些有影响的点把他离散化,接着每一个区间异或一下,因为是区间,所以用差分来进行区间异或。然后异或方法就是前面的几个区间。最后再找到最大的答案就可以了。
然后要注意的在修改第三个奖励条件的时候,修改
点击查看代码
struct node{
int type;
int a;
int l,r;
};
int n;
node w[N];
set<int> s;
int h[N * 4];
int ans[N * 4];
// 差分
// 更改区间
void f(int l,int r,int x) {
ans[l] ^= x;
ans[r + 1] ^= x;
}
int main(){
n = fr();
int a,b,c;
int type;
for (int i = 1; i <= n; i ++) {
type = fr();
if (type == 1) {
a = fr(),b = fr(),c = fr();
w[i] = {type,c,a,b};
s.insert(a - 1);
s.insert(a);
s.insert(b);
s.insert(b + 1);
} else {
a = fr(),b = fr();
w[i] = {type,b,a,0};
s.insert(a);
s.insert(a + 1);
s.insert(a - 1);
}
}
s.insert(inf);
s.insert(-inf);
s.insert(0);
int cnt = 0;
for (auto i : s) {
h[++ cnt] = i;
}
for (int i = 1; i <= n; i ++) {
w[i].l = lower_bound(h + 1,h + 1 + cnt,w[i].l) - h;
if (w[i].type == 1) {
w[i].r = lower_bound(h + 1,h + 1 + cnt,w[i].r) - h;
f(w[i].l,w[i].r,w[i].a);
} else if (w[i].type == 2) {
f(w[i].l,w[i].l,w[i].a);
} else {
f(1,w[i].l - 1,w[i].a);
f(w[i].l + 1,cnt,w[i].a);
}
}
int res = ans[1],num = 1;
for (int i = 2; i <= cnt; i ++) {
ans[i] ^= ans[i - 1];
if (res < ans[i]) res = ans[i],num = i;
else if (res == ans[i] && abs(h[num]) > abs(h[i])) num = i;
else if (res == ans[i] && abs(h[num]) == abs(h[i]) && num < i)
num = i;
}
fw(res),kg;
fw(h[num]);
return 0;
}
4766 Outer space invaders
区间
因为
点击查看代码
struct node{
int a,b,d;
};
int n;
node w[N];
set<int> s;
int h[10004];
int dp[N * 2][N * 2]; // 消灭这个时间内所有的外星人
bool cmp(node a,node b) {
if (a.a == b.a) return a.b < b.b;
return a.a < b.a;
}
int main(){
int T = fr();
while (T --) {
n = fr();
memset(dp,0,sizeof dp);
s.clear();
for (int i = 1; i <= n; i ++) {
w[i].a = fr();
w[i].b = fr();
w[i].d = fr();
s.insert(w[i].b);
s.insert(w[i].a);
}
sort(w + 1,w + 1 + n,cmp);
int cnt = 0;
for (auto &i : s) {
h[i] = ++ cnt;
}
for (int len = 2; len <= cnt; len ++) {
for (int l = 1; l <= cnt; l ++) {
int r = l + len - 1;
if (r > cnt) break;
int id = 0;
for (int i = 1; i <= n; i ++) {
if (h[w[i].a] >= l && h[w[i].b] <= r) {
if (!id || w[i].d > w[id].d) id = i;
}
}
if (!id) dp[l][r] = 0;
else {
dp[l][r] = inf;
for (int t = h[w[id].a]; t <= h[w[id].b]; t ++) {
// 什么时候开炮
dp[l][r] = min(dp[l][r],dp[l][t - 1] + dp[t + 1][r]);
}
dp[l][r] += w[id].d;
}
}
}
int ans = dp[1][cnt];
fw(ans);
ch;
}
return 0;
}
Day 26
6538 LOPOV
一遍过捏。
点击查看代码
int n,k,maxn;
pii w[N];
multiset<int> s;
bool cmp(pii a,pii b) {
return a.se > b.se;
}
int main(){
n = fr(),k = fr();
for (int i = 1; i <= n; i ++) {
w[i].fi = fr();
w[i].se = fr();
}
for (int i = 1; i <= k; i ++) {
s.insert(fr());
}
sort(w + 1,w + 1 + n);
lwl ans = 0;
int u = 1;
w[n + 1].fi = inf;
priority_queue<int> q;
for (auto m : s) {
while (w[u].fi <= m) {
q.push(w[u].se);
u ++;
}
if (q.size()) {
ans += q.top();
q.pop();
}
}
fw(ans);
return 0;
}
6659 Najmniejsza
要求连续区间的
解决了两个数,后面就处理三个数的。通过打表可以知道三个数的乘积如果不超过
点击查看代码
lwl a;
map<lwl,pii> w;
lwl gcd(lwl x,lwl y) {
if (!y) return x;
return gcd(y,x % y);
}
lwl lcm(int l,int r) {
lwl u = 1;
for (int i = l; i <= r; i ++) {
u = u / gcd(u,i) * i;
}
return u;
}
// 预处理
void init() {
for (lwl l = 1; l < N; l ++) {
lwl u = l * (l + 1);
for (lwl r = l + 2; ; r ++) {
u /= gcd(u,r);
if (u > linf / r) break;
u *= r;
if (!w[u].fi) w[u] = {l,r};
}
}
}
int main(){
int T = fr();
init();
while (T --) {
a = fr();
if (a % 2) {
wj;
continue;
}
lwl t = sqrt(a);
if (t * (t + 1) == a) {
if (w[a].fi) {
fw(w[a].fi),kg;
fw(w[a].se),ch;
}
else {
if (t == 2 || t == 3) fw(1);
else fw(t);
kg;
fw(t + 1),ch;
}
continue;
}
if (a % 3) {
wj;
continue;
}
if (w[a].fi) {
fw(w[a].fi),kg;
fw(w[a].se),ch;
} else wj;
}
return 0;
}
6600 字母
感觉和
这一题先处理每一个点向左边,右边和下面延伸的话最多可以延伸多少个
最后再枚举每一个中心所对应的大
(这道题的
点击查看代码
int n,m;
int a,b,s,x;
bool w[N][N];
int l[N][N],r[N][N],down[N][N];
int val[N][N];
int main(){
n = fr(),m = fr();
a = fr(),b = fr(),s = fr(),x = fr();
a = max(3,a),b = max(2,b);
if (a % 2 == 0) a ++;
string ss;
for (int i = 1; i <= n; i ++) {
cin >> ss;
for (int j = 0; j < m; j ++) {
if (ss[j] == '1') {
w[i][j + 1] = true;
l[i][j + 1] = l[i][j] + 1;
}
}
}
for (int i = n; i; i --) {
for (int j = m; j; j --) {
if (w[i][j]) {
r[i][j] = r[i][j + 1] + 1;
down[i][j] = down[i + 1][j] + 1;
}
}
}
// 枚举 T 的形状,记录前缀和
for (int i = a; i <= m; i += 2) {
for (int j = b; j <= n; j ++) {
if (i + j >= x && i * j >= s)
val[i][j] = val[i - 2][j] + val[i][j - 1] - val[i - 2][j - 1] + 1;
}
}
lwl ans = 0;
// 枚举每一个中心然后计算
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
if (!w[i][j]) continue;
ans += val[min(l[i][j],r[i][j]) * 2 - 1][down[i][j]];
}
}
fw(ans);
return 0;
}
4909 Ski Lift G
一开始题目读不懂,寄了。
代码意思挺好懂的,但是有两点要注意一下:1. 起点处是必须要建观测站的,初始化的时候初始化为
点击查看代码
int n,k;
int h[N];
int dp[N];
double get(int i,int j) {
return 1.0 * (h[i] - h[j]) / (j - i);
}
int main(){
n = fr(),k = fr();
for (int i = 1; i <= n; i ++) {
h[i] = fr();
}
memset(dp,0x3f,sizeof dp);
dp[1] = 1;
for (int i = 2; i <= n; i ++) {
double maxn = -inf;
for (int j = i - 1; j >= max(1,i - k); j --) {
double t = get(i,j);
if (t >= maxn) {
maxn = t;
dp[i] = min(dp[i],dp[j] + 1);
}
}
}
fw(dp[n]);
return 0;
}
4377 Talent Show G
然后补题的时候又把
由题目意思可以想到用
点击查看代码
int n,m;
pii w[N];
lwl dp[1005];
bool cmp(pii a,pii b) {
return a.se * b.fi > a.fi * b.se;
}
bool check(int x) {
memset(dp,-0x3f,sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = m; j >= 0; j --) {
int t = min(j + w[i].fi,m);
dp[t]=max(dp[t],dp[j]+w[i].se - (lwl)w[i].fi * x);
}
}
return dp[m] >= 0;
}
int main(){
n = fr(),m = fr();
int l = 0,r = -inf;
for (int i = 1; i <= n; i ++) {
w[i].fi = fr(),w[i].se = fr();
w[i].se *= 1000;
r = max(r,w[i].se / w[i].fi);
}
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
fw(l);
return 0;
}
Day 27
Coloring Brackets
区间
点击查看代码
int n;
char s[N];
int ma[N];
lwl dp[N][N][4][4];
void dfs(int l,int r) {
if (r == l + 1) {
int i = r;
dp[i - 1][i][2][0] = 1;
dp[i - 1][i][0][2] = 1;
dp[i - 1][i][1][0] = 1;
dp[i - 1][i][0][1] = 1;
return ;
}
if (dp[l][r][1][0]) {
return ;
}
if (ma[l] != r) {
dfs(l,ma[l]);
dfs(ma[l] + 1,r);
for (int i = 0; i <= 2; i ++)
for (int j = 0; j <= 2; j ++)
for (int a = 0; a <= 2; a ++)
for (int b = 0; b <= 2; b ++) {
if (j == a && j != 0) break;
dp[l][r][i][b] += dp[l][ma[l]][i][j] * dp[ma[l] + 1][r][a][b] % mod;
dp[l][r][i][b] %= mod;
}
return ;
}
int i = r - 1,j = l + 1;
dfs(j,i);
dp[l][r][0][1] = (dp[j][i][0][2] + dp[j][i][1][0] + dp[j][i][2][0] + dp[j][i][1][2] + dp[j][i][0][0] + dp[j][i][2][2]) % mod;
dp[l][r][0][2] = (dp[j][i][0][1] + dp[j][i][1][0] + dp[j][i][2][0] + dp[j][i][2][1] + dp[j][i][0][0] + dp[j][i][1][1]) % mod;
dp[l][r][1][0] = (dp[j][i][0][1] + dp[j][i][0][2] + dp[j][i][2][0] + dp[j][i][2][1] + dp[j][i][0][0] + dp[j][i][2][2]) % mod;
dp[l][r][2][0] = (dp[j][i][0][1] + dp[j][i][1][0] + dp[j][i][0][2] + dp[j][i][1][2] + dp[j][i][0][0] + dp[j][i][1][1]) % mod;
}
int main(){
scanf("%s",s + 1);
n = strlen(s + 1);
stack<int> st;
for (int i = 1; i <= n; i ++) {
if (s[i] == '(') st.push(i);
else {
ma[i] = st.top();
ma[st.top()] = i;
st.pop();
}
}
dfs(1,n);
lwl ans = 0;
for (int i = 0; i <= 2; i ++) {
for (int j = 0; j <= 2; j ++)
ans += dp[1][n][i][j];
}
ans %= mod;
fw(ans);
return 0;
}
248 G
区间
点击查看代码
int n;
int w[N];
int dp[N][N];
int main(){
n = fr();
for (int i = 1; i <= n; i ++)
w[i] = fr();
for (int i = 1; i <= n; i ++) dp[i][i] = w[i];
for (int len = 2; len <= n; len ++) {
for (int l = 1; l <= n; l ++) {
int r = l + len - 1;
if (r > n) break;
for (int k = l; k < r; k ++) {
if (dp[l][k] == dp[k + 1][r] && dp[l][k]) {
dp[l][r] = max(dp[l][r],dp[l][k] + 1);
}
}
}
}
int ans = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++)
ans = max(ans,dp[i][j]);
}
fw(ans);
return 0;
}
8675 搭积木
前缀和+区间
点击查看代码
int n,m;
int flag[N][N];
lwl dp[N][N][N];
lwl sum[N][N];
int main(){
n = fr(), m = fr();
for (int i = 1; i <= n; i ++) {
string s;
cin >> s;
for (int j = 1; j <= m; j ++) {
if (s[j - 1] == '.') flag[i][j] = flag[i][j - 1];
else flag[i][j] = flag[i][j - 1] + 1;
}
}
lwl ans = 0;
for (int l = 1; l <= m; l ++) {
for (int r = l; r <= m; r ++) {
if (flag[n][r] == flag[n][l - 1]) {
dp[n][l][r] = 1;
ans ++;
}
}
}
for (int i = n - 1; i; i --) {
for (int l = 1; l <= m; l ++) {
for (int r = l; r <= m; r ++) {
sum[l][r] = dp[i + 1][l][r] + sum[l][r - 1] + sum[l - 1][r] - sum[l - 1][r - 1];
sum[l][r] %= mod;
}
}
for (int l = 1; l <= m; l ++) {
for (int r = l; r <= m; r ++) {
if (flag[i][r] != flag[i][l - 1]) continue;
dp[i][l][r] = sum[l][m] - sum[l][r - 1];
dp[i][l][r] %= mod;
ans = (ans + dp[i][l][r]) % mod;
}
}
}
ans = (ans + 1 + mod) % mod;
fw(ans);
return 0;
}
イウィ
也比较板子(,判断当前区间能不能合并,然后如果合并的话中间的一定是要消掉的,还有就是先将当前区间的
点击查看代码
int n;
char s[N];
int dp[N][N];
int main(){
scanf("%s", (s + 1));
n = strlen(s + 1);
for (int len = 3; len <= n; len ++) {
for (int l = 1; l <= n; l ++) {
int r = l + len - 1;
if (r > n) break;
for (int k = l; k < r; k ++)
dp[l][r] = max(dp[l][r],dp[l][k] + dp[k + 1][r]);
if (s[r] == 'w' || s[l] == 'w') continue;
for (int k = l + 1; k < r; k ++) {
if (s[k] == 'i') continue;
if (dp[l + 1][k - 1] * 3 >= (k - l - 1) && dp[k + 1][r - 1] * 3 >= (r - k - 1))
dp[l][r] = max(dp[l][r], dp[l + 1][k - 1] + dp[k + 1][r - 1] + 1);
}
}
}
int ans = 0;
for (int l = 1; l <= n; l ++) {
for (int r = l; r <= n; r ++)
ans = max(ans,dp[l][r]);
}
fw(ans);
ch;
return 0;
}
Letter Pickings
可以证明
点击查看代码
int n;
char s[N];
bool dp[N][N];
int main(){
int T = fr();
while (T --) {
memset(dp,0,sizeof dp);
scanf("%s",s + 1);
n = strlen(s + 1);
for (int len = 2; len <= n; len ++) {
for (int l = 1; l <= n; l ++) {
int r = l + len - 1;
if (r > n) break;
if ((s[l] > s[l + 1] || dp[l + 2][r]) &&
(s[l] > s[r] || dp[l + 1][r - 1]))
dp[l][r] = true;
if ((s[r] > s[r - 1] || dp[l][r - 2]) &&
(s[r] > s[l] || dp[l + 1][r - 1]))
dp[l][r] = true;
}
}
if (dp[1][n]) ay;
else pj;
}
return 0;
}
Day 28
Zuma
一遍过捏,直接区间
点击查看代码
int n;
int w[N];
int dp[N][N];
int main(){
n = fr();
memset(dp,0x3f,sizeof dp);
for (int i = 1; i <= n; i ++) {
w[i] = fr();
dp[i][i] = 1;
if (w[i] == w[i - 1]) dp[i - 1][i] = 1;
else dp[i - 1][i] = 2;
}
for (int len = 3; len <= n; len ++) {
for (int l = 1; l <= n; l ++) {
int r = l + len - 1;
if (r > n) break;
if (w[r] == w[l]) dp[l][r] = dp[l + 1][r - 1];
for (int k = l; k < r; k ++)
dp[l][r] = min(dp[l][r],dp[l][k] + dp[k + 1][r]);
}
}
fw(dp[1][n]);
return 0;
}
1220 关路灯
一遍过捏。一开始有一个地方的
点击查看代码
int n,st;
lwl dp[N][N][2];
// 0 在左端点
// 1 在右端点
pii w[N];
lwl sum[N];
lwl get(int l,int r) {
if (l > r) swap(l,r);
return sum[n] - (sum[r] - sum[l - 1]);
}
lwl dis(int a,int b) {
return abs(w[a].fi - w[b].fi);
}
int main(){
n = fr(), st = fr();
for (int i = 1; i <= n; i ++) {
w[i] = {fr(), fr()};
sum[i] = sum[i - 1] + w[i].se;
}
memset(dp,0x3f,sizeof dp);
dp[st][st][0] = dp[st][st][1] = 0;
for (int len = 2; len <= n; len ++) {
for (int l = 1; l <= n; l ++) {
int r = l + len - 1;
if (r > n) break;
dp[l][r][0] = min(dp[l + 1][r][0] + dis(l + 1,l) * get(l + 1,r),dp[l + 1][r][1] + dis(r,l) * get(l + 1,r));
dp[l][r][1] = min(dp[l][r - 1][0] + dis(l,r) * get(r - 1,l),dp[l][r - 1][1] + dis(r - 1,r) * get(r - 1,l));
int t = 0;
}
}
fw(min(dp[1][n][0],dp[1][n][1]));
return 0;
}
9119 圣诞树
一遍过捏。区间
点击查看代码
int n,k;
pdd w[N];
double dp[N][N][2];
int pre[N][N][2];
int id(int i) {
if (i > n) return i - n;
if (i <= 0) return i + n;
return i;
}
double dis(int a,int b) {
int i = id(a), j = id(b);
double dx = w[i].fi - w[j].fi;
double dy = w[i].se - w[j].se;
return sqrt(dx * dx + dy * dy);
}
int main(){
n = fr();
double maxn = -dinf;
for (int i = 1; i <= n; i ++) {
cin >> w[i].fi;
cin >> w[i].se;
if (maxn < w[i].se) {
maxn = w[i].se;
k = i;
}
}
for (int l = 0; l < n; l ++) {
for (int r = 0; r < n; r ++)
dp[l][r][0] = dp[l][r][1] = dinf;
}
dp[0][0][1] = dp[0][0][0] = 0;
memset(pre,-1,sizeof pre);
for (int len = 1; len < n; len ++) {
for (int l = 0; l <= len; l ++) {
// 向左延伸 & 向右延伸
int r = len - l;
if (l && dp[l][r][0] > dp[l - 1][r][0] + dis(k-l+1,k - l)) {
dp[l][r][0] = dp[l - 1][r][0] + dis(k - l + 1,k - l);
pre[l][r][0] = 0;
}
if (l && dp[l][r][0] > dp[l - 1][r][1] + dis(k - l,k + r)) {
dp[l][r][0] = dp[l - 1][r][1] + dis(k - l,k + r);
pre[l][r][0] = 1;
}
if (r && dp[l][r][1] > dp[l][r - 1][1] + dis(k+r-1,k + r)) {
dp[l][r][1] = dp[l][r - 1][1] + dis(k + r - 1,k + r);
pre[l][r][1] = 1;
}
if (r && dp[l][r][1] > dp[l][r - 1][0] + dis(k - l,k + r)) {
dp[l][r][1] = dp[l][r - 1][0] + dis(k - l,k + r);
pre[l][r][1] = 0;
}
}
}
double ans = dinf;
int L,R,t;
for (int l = 0; l < n; l ++) {
int r = n - 1 - l;
if (dp[l][r][0] < ans) {
ans = dp[l][r][0];
L = l, R = r, t = 0;
}
if (dp[l][r][1] < ans) {
ans = dp[l][r][1];
L = l, R = r, t = 1;
}
}
stack<int> path;
while (~pre[L][R][t]) {
if (t) {
path.push(k + R);
t = pre[L][R][t];
R = R - 1;
} else {
path.push(k - L);
t = pre[L][R][t];
L = L - 1;
}
}
path.push(k);
while (path.size()) {
fw(id(path.top()));
kg;
path.pop();
}
return 0;
}
Make Pair
在断网的时候死活想不出来该怎么去去重,然后发现原来做法都有点问题,恼。
用这种组合数学的方法的话就不需要去重,用一下组合数就可以了。
点击查看代码
int n,m;
set<int> s[N];
lwl dp[N][N];
lwl f[N][N];
lwl C[N][N];
void init() {
C[0][0] = 1;
for (int i = 1; i < N; i ++) {
C[i][0] = 1;
for (int j = 1; j <= i; j ++) {
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
}
int main(){
n = fr(), m = fr();
for (int i = 1; i <= m; i ++) {
int a = fr(),b = fr();
if (a > b) swap(a,b);
s[a].insert(b);
if (b - a == 1) dp[a][b] = dp[b][a] = 1;
}
init();
for (int len = 4; len <= n * 2; len += 2) {
int t = len >> 1;
for (int l = 1; l <= n * 2; l ++) {
int r = len + l - 1;
if (r > n * 2) break;
if (s[l].find(r) != s[l].end())
dp[l][r] = dp[l + 1][r - 1];
for (int k = l + 2; k < r; k += 2) {
if (s[k].find(r) != s[k].end()) {
dp[l][r] += dp[l][k - 1] * dp[k + 1][r - 1] % mod * C[t][(r - k + 1) / 2] % mod;
dp[l][r] %= mod;
} else if (s[l].find(k) != s[l].end()) {
dp[l][r] += dp[l + 1][k - 1] * dp[k + 1][r] % mod * C[t][(k - l + 1) / 2] % mod;
dp[l][r] %= mod;
}
}
}
}
fw(dp[1][2 * n]);
ch;
return 0;
}
Day 29
7914 括号序列
分六种情况存储:
=> 对应的是全部都是 的情况(也就是说存一下当前区间是不是 ) => 对应的是左右两边分别是左括号和右括号,并且这两个括号互相对应 => 对应的是 => 对应的是 ( 的情况包括在内) => 对应的是 => 两侧都是 的情况( 的情况包括在内)
然后再按照这种存储转移就行了,然后显然
然后代码中更新
点击查看代码
int n,k;
int w[N];
lwl dp[N][N][6];
// 0 *** 1 (...) 2 (...)**(...)**
// 3 (...)**(...) 4 **(...)**(...) 5 **(...)...(...)**
int main(){
n = fr(), k = fr();
string s;
cin >> s;
for (int i = 1; i <= n; i ++) {
if (s[i - 1] == '(') w[i] = 1;
else if (s[i - 1] == ')') w[i] = 2;
else if (s[i - 1] == '*') w[i] = -2;
else w[i] = -1;
}
for (int i = 0; i < n; i ++) dp[i + 1][i][0] = 1;
for (int len = 1; len <= n; len ++) {
for (int l = 1; ; l ++) {
int r = l + len - 1;
if (r > n) break;
if (len <= k && w[l] < 0)
dp[l][r][0] = dp[l + 1][r][0];
if (len >= 2) {
if ((w[l] == 1 || w[l] == -1) && (w[r] == -1 || w[r] == 2))
dp[l][r][1] = (dp[l + 1][r - 1][0] + dp[l + 1][r - 1][2] + dp[l + 1][r - 1][3] + dp[l + 1][r - 1][4]) % mod;
for (int k = l; k < r; k ++) {
dp[l][r][2] = (dp[l][r][2] + dp[l][k][3] * dp[k + 1][r][0]) % mod;
dp[l][r][3] = (dp[l][r][3] + (dp[l][k][2] + dp[l][k][3]) * dp[k + 1][r][1]) % mod;
dp[l][r][4] = (dp[l][r][4] + dp[l][k][0] * dp[k + 1][r][3]) % mod;
dp[l][r][5] = (dp[l][r][5] + dp[l][k][4] * dp[k + 1][r][0]) % mod;
}
}
dp[l][r][3] = (dp[l][r][3] + dp[l][r][1]) % mod;
dp[l][r][5] = (dp[l][r][5] + dp[l][r][0]) % mod;
}
}
fw(dp[1][n][3]);
return 0;
}
6879 スタンプラリー 3
在信友队的时候做过这道题,一遍过捏()
点击查看代码
int n,L;
pii w[N];
int dp[N][N][N][2];
void mn(int &a,int b) {
if (a > b) a = b;
}
int dis(int a,int b) {
return abs(w[a].fi - w[b].fi);
}
signed main(){
n = fr(), L = fr();
for (int i = 1; i <= n; i ++)
w[i].fi = fr();
for (int i = 1; i <= n; i ++)
w[i].se = fr();
memset(dp,0x3f,sizeof dp);
dp[0][0][0][0] = dp[0][0][0][1] = 0;
w[n + 1] = {L,-inf};
for (int l = 0; l <= n; l ++) {
for (int r = 0; r <= n; r ++) {
if (l + r >= n) break;
for (int k = 0; k <= n; k ++) {
int u = dp[l][r][k][0];
int fl;
if (u + dis(n - l + 1,n - l) <= w[n - l].se)
fl = 1;
else fl = 0;
mn(dp[l + 1][r][k + fl][0],u + dis(n - l + 1,n - l));
if (u + L - dis(r + 1,n - l + 1) <= w[r + 1].se)
fl = 1;
else fl = 0;
mn(dp[l][r + 1][k + fl][1],u + L - dis(r + 1,n - l + 1));
u = dp[l][r][k][1];
if (u + dis(r + 1,r) <= w[r + 1].se)
fl = 1;
else fl = 0;
mn(dp[l][r + 1][k + fl][1],u + dis(r + 1,r));
if (u + L - dis(r,n - l) <= w[n - l].se)
fl = 1;
else fl = 0;
mn(dp[l + 1][r][k + fl][0],u + L - dis(r,n - l));
}
}
}
for (int k = n; k; k --) {
for (int i = 0; i <= n; i ++) {
for (int j = 0; j <= n; j ++) {
if (dp[i][j][k][0] < linf || dp[i][j][k][1] < linf) {
fw(k);
return 0;
}
}
}
}
fw(0);
return 0;
}
ACwing1222 密码脱落
区间
如果当前的
点击查看代码
int n;
int w[N];
int dp[N][N];
int main(){
string s;
cin >> s;
n = s.length();
memset(dp,0x3f,sizeof dp);
for (int i = 1; i <= n; i ++) {
if (s[i - 1] == 'A') w[i] = 1;
else if (s[i - 1] == 'B') w[i] = 2;
else if (s[i - 1] == 'C') w[i] = 3;
else w[i] = 4;
dp[i][i] = 0;
dp[i][i - 1] = 0;
}
for (int len = 2; len <= n; len ++) {
for (int l = 1; ; l ++) {
int r = l + len - 1;
if (r > n) break;
if (w[l] == w[r]) dp[l][r] = dp[l + 1][r - 1];
else dp[l][r] = min(dp[l + 1][r],dp[l][r - 1]) + 1;
}
}
fw(dp[1][n]);
return 0;
}
3147 262144 P
是前面那道
点击查看代码
int n;
int w[N][100];
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
int a = fr();
w[i][a] = i + 1;
}
int ans = 0;
for (int j = 1; j <= 58; j ++) {
for (int i = 1; i <= n; i ++) {
if (!w[i][j])
w[i][j] = w[w[i][j - 1]][j - 1];
if (w[i][j]) ans = j;
}
}
fw(ans);
return 0;
}
ABC 252G Pre-Order
区间
因为题目中说了如果同为一个节点的儿子节点先遍历编号小的,所以在转移的时候需要加一个判断:
或者说
点击查看代码
int n;
int p[N];
lwl dp[N][N];
int main(){
n = fr();
for (int i = 1; i <= n; i ++) p[i] = fr();
for (int i = 1; i <= n; i ++) dp[i][i] = 1;
for (int len = 2; len <= n; len ++) {
for (int l = 1; ; l ++) {
int r = l + len - 1;
if (r > n) break;
for (int k = l; k <= n; k ++) {
if (p[l + 1] <= p[k + 1] || k == r)
dp[l][r] = (dp[l][r] + dp[l + 1][k] * dp[k][r] % mod) % mod;
}
}
}
fw(dp[1][n]);
return 0;
}
4539 zh_tree
一眼
初始化的话把所有值搞到最大值就可以了,然后
然后这里
点击查看代码
int n;
double t,c;
int w[N];
double f[N];
double dp[N][N][N];
// l r => 区间
// h => 这个区间的最浅深度
double dfs(int l,int r,int h) {
if (l > r) return 0;
if (dp[l][r][h] < dinf) return dp[l][r][h];
for (int k = l; k <= r; k ++) {
double temp = dp[k][k][h];
// 以 k 为根
temp += dfs(l,k - 1,h + 1);
temp += dfs(k + 1,r,h + 1);
dp[l][r][h] = min(dp[l][r][h],temp);
}
return dp[l][r][h];
}
int main(){
n = fr();
cin >> t >> c;
int sum = 0;
for (int i = 1; i <= n; i ++) {
w[i] = fr();
sum += w[i];
}
for (int i = 1; i <= n; i ++) {
f[i] = 1.0 * w[i] / sum;
}
for (int i = 1; i <= n; i ++) {
for (int k = 1; k <= n; k ++) {
for (int j = 1; j <= n; j ++) {
dp[i][j][k] = dinf;
}
dp[i][i][k] = (k * t + c) * f[i];
}
}
dfs(1,n,1);
printf("%.3lf",dp[1][n][1]);
return 0;
}
Day 30
AGC058B Adjacent Chmax
这个题一开始做的时候,完全不会做吧,然后就直接把这一题放弃了跑去做第三题了
然后开网之后看题解,我草,这么短的代码,比我在一开始写的时候写的预处理都要短。
正解也是先用单调栈处理出左边和右边第一个比这个数大的数在哪里(记录的时候记录比这个数大的数的前一个或者后一个(也就是记录一下可以延续的区间))
然后处理之后就可以直接转移了。(一开始写单调栈的时候又把 while
写成 if
了!!!)
点击查看代码
int n;
int w[N];
int L[N],R[N];
lwl dp[N];
void init() {
stack<int> s;
for (int i = 1; i <= n; i ++) {
while (s.size() && w[s.top()] < w[i]) s.pop();
if (s.size()) L[i] = s.top() + 1;
else L[i] = 1;
s.push(i);
}
while (s.size()) s.pop();
for (int i = n; i; i --) {
while (s.size() && w[s.top()] < w[i]) s.pop();
if (s.size()) R[i] = s.top() - 1;
else R[i] = n;
s.push(i);
}
}
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
init();
dp[0] = 1;
for (int i = 1; i <= n; i ++) {
for (int j = L[i]; j <= R[i]; j ++)
dp[j] = (dp[j] + dp[j - 1]) % mod;
}
fw(dp[n]);
return 0;
}
5336 成绩单
区间
然后因为这个
点击查看代码
int a, b;
int n, idx;
int w[N], y[N];
int h[1005];
lwl dp[N][N][N][N];
// l r minn maxn
// 取出 [l, r] , 极值为 minn , maxn 的最小代价
lwl cost[N][N]; // l,r 的最小代价
void init() {
memset(dp,0x3f,sizeof dp);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= w[i]; j ++) {
for (int k = w[i]; k <= idx; k ++) {
dp[i][i][j][k] = 0;
}
}
}
memset(cost,0x3f,sizeof cost);
for (int i = 1; i <= n; i ++) cost[i][i] = a;
}
lwl get(int i,int j) {
return a + b * (y[j] - y[i]) * (y[j] - y[i]);
}
void dfs(int l,int r) {
if (cost[l][r] < linf) return ;
for (int k = l; k < r; k ++) {
dfs(l,k), dfs(k + 1,r);
for (int i = 1; i <= idx; i ++) {
for (int j = i; j <= idx; j ++) {
lwl &u = dp[l][r][i][j];
// 枚举极值在的三种地方
// 在左区间
u = min(u,dp[l][k][i][j] + cost[k + 1][r]);
// 在右区间
u = min(u,cost[l][k] + dp[k + 1][r][i][j]);
// 左右都有
u = min(u,dp[l][k][i][j] + dp[k + 1][r][i][j]);
// 更新答案
cost[l][r] = min(cost[l][r],u + get(i,j));
}
}
}
}
int main(){
n = fr();
a = fr(), b = fr();
// 离散化
for (int i = 1; i <= n; i ++) {
w[i] = fr();
y[i] = w[i];
}
sort(y + 1,y + 1 + n);
unique(y + 1,y + 1 + n);
for (int i = 1; i <= n; i ++) {
if (!h[y[i]]) h[y[i]] = ++ idx;
}
for (int i = 1; i <= n; i ++) {
w[i] = h[w[i]];
}
init();
dfs(1,n);
lwl ans = cost[1][n];
fw(ans);
return 0;
}
5052 Go
一开始断网的时候写的代码只有
就这个代码如果起点比小精灵所在的号码最大的房子要大的话,他是没有更新起点的,所以要加一个特判。
点击查看代码
struct node{
int a,b,t;
};
int n, k, m;
node w[N];
int dp[N][N][2005][2];
int ans = 0;
int dis(int x,int y) {
return abs(w[x].a - w[y].a);
}
void mx(int &a, int b) {
if (a < b) a = b;
}
int main(){
n = fr(), k = fr(), m = fr();
int maxn = 0;
int st = 0;
for (int i = 1; i <= m; i ++) {
w[i] = {fr(), fr(), fr()};
maxn = max(maxn,w[i].t);
if (w[i].a > k && w[i - 1].a < k) {
w[i + 1] = w[i];
w[i] = {k,0,0};
st = i;
m ++;
i ++;
} else if (w[i].a == k) st = i;
}
if (!st) {
m ++;
w[m] = {k,0,0};
st = m;
}
for (int len = 1; len <= m; len ++) {
for (int l = 0; l <= len && st - l > 0; l ++) {
int r = len - l;
if (st + r > m) continue;
for (int t = maxn; t >= 0; t --) {
dp[l][r][t][0] = dp[l][r][t][1] = -inf;
int fl = 0;
if (t < w[st - l].t) fl = 1;
int fr1 = 0;
if (t < w[st + r].t) fr1 = 1;
int u;
if (l)
u = t - dis(st - l,st - l + 1);
if (u >= 0 && l)
mx(dp[l][r][t][0], dp[l - 1][r][u][0] + w[st - l].b * fl);
if (r)
u = t - dis(st + r,st + r - 1);
if (u >= 0 && r)
mx(dp[l][r][t][1], dp[l][r - 1][u][1] + w[st + r].b * fr1);
u = t - dis(st - l, st + r);
if (u >= 0) {
if (r)
mx(dp[l][r][t][1], dp[l][r - 1][u][0] + w[st + r].b * fr1);
if (l)
mx(dp[l][r][t][0], dp[l - 1][r][u][1] + w[st - l].b * fl);
}
ans = max(ans,max(dp[l][r][t][0], dp[l][r][t][1]));
}
}
}
ans += w[st].b;
fw(ans);
return 0;
}
ABC163E Active infants
先排个序,然后跑一遍区间
点击查看代码
int n;
pii w[N];
lwl dp[N][N];
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i].fi = fr();
w[i].se = i;
}
sort(w + 1,w + 1 + n);
for (int len = 1; len <= n; len ++) {
for (int l = 1; ; l ++) {
int r = l + len - 1;
if (r > n) break;
dp[l][r] = max(dp[l][r],dp[l + 1][r] + (lwl)w[len].fi * abs(w[len].se - l));
dp[l][r] = max(dp[l][r],dp[l][r - 1] + (lwl)w[len].fi * abs(w[len].se - r));
}
}
fw(dp[1][n]);
return 0;
}
4302 字符串折叠
这个题目因为要求一样的才能折叠,所以为了后面方便匹配可以先预处理一下哈希(但是好像不用预处理直接暴力匹配也可以过),然后在转移的时候分两种情况讨论。
- 不发生新的折叠,那么就是直接枚举断点,将左半边和右半边加在一起就是答案了。
- 在这个区间发生之前没有的折叠,因为要折叠的话就把整段都折叠起来,不然就和之前的状态会有重复,所以就枚举把这一段分成多少小段(这个段数和区间长度要能够整除!),判断的话就用哈希就可以了。然后前面还处理一下每一个数字的长度,这个地方就可以写成(
是每一段的长度):
点击查看代码
const int P = 2333;
int n;
int Len[N];
int w[N];
lwl hsh[N], p[N];
int dp[N][N];
char s[N];
void init() {
for (int i = 1; i <= 9; i ++) Len[i] = 3;
for (int i = 10; i <= 99; i ++) Len[i] = 4;
Len[100] = 5;
p[0] = 1;
for (int i = 1; i <= n; i ++) {
hsh[i] = hsh[i - 1] * P + s[i];
p[i] = p[i - 1] * P;
}
}
lwl get(int l,int r) {
return hsh[r] - hsh[l - 1] * p[r - l + 1];
}
bool fold(int l,int r,int len) {
lwl t = 0;
for (int i = l, j = l + len - 1; j <= r; i += len, j += len) {
if (!t) t = get(i,j);
else if (get(i,j) != t) return false;
}
return true;
}
int main(){
scanf("%s",(s + 1));
n = strlen(s + 1);
memset(dp,0x3f,sizeof dp);
for (int i = 1; i <= n; i ++) {
dp[i][i] = 1;
}
init();
for (int len = 2; len <= n; len ++) {
for (int l = 1; ; l ++) {
int r = l + len - 1;
if (r > n) break;
// 不新折叠
for (int k = l; k < r; k ++)
dp[l][r] = min(dp[l][r],dp[l][k] + dp[k + 1][r]);
// 折叠(整段折叠)
for (int k = 1; k <= len; k ++) {
if (len % k) continue;
if (fold(l,r,k))
dp[l][r] = min(dp[l][r],dp[l][l + k - 1] + Len[len / k]);
}
}
}
fw(dp[1][n]);
return 0;
}
2470 压缩
这一题的状态设置好妙。
这样设置状态是因为这一题的
于是在转移的时候,我们也分成两种情况来转移,第一种就是这个区间里面没有
而我们假设前面有一个
这些就是当这个区间内没有
(一开始长度为 1 的
点击查看代码
int n;
char s[N];
int dp[N][N][3];
// 0 => no M
// 1 => have M
// 假设在 dp[l][r] 时 l - 1 处已经有了一个 M
// 2 => min(0,1)
lwl hsh[N],p[N];
void init() {
memset(dp,0x3f,sizeof dp);
p[0] = 1;
for (int i = 1; i <= n; i ++) {
hsh[i] = hsh[i - 1] * P + s[i] - 'a' + 1;
p[i] = p[i - 1] * P;
}
for (int i = 1; i <= n; i ++) {
dp[i][i][0] = 1;
dp[i][i][1] = 2;
dp[i][i][2] = 1;
}
}
lwl get(int l,int r) {
return hsh[r] - hsh[l - 1] * p[r - l + 1];
}
int main(){
scanf("%s",(s + 1));
n = strlen(s + 1);
init();
for (int len = 2; len <= n; len ++) {
for (int l = 1; ; l ++) {
int r = l + len - 1;
if (r > n) break;
for (int k = l; k < r; k ++)
// 只有 l - 1 处有 M,所以后面只能不压缩
dp[l][r][0] = min(dp[l][r][0], dp[l][k][0] + r - k);
int t = l + (len >> 1) - 1;
if ((!(len & 1)) && (get(l,t) == get(t + 1,r)))
dp[l][r][0] = min(dp[l][r][0], dp[l][t][0] + 1);
for (int k = l; k < r; k ++) {
// 枚举在哪里放 M
dp[l][r][1] = min(dp[l][r][1],dp[l][k][2] + dp[k + 1][r][2] + 1);
}
dp[l][r][2] = min(dp[l][r][1],dp[l][r][0]);
}
}
fw(dp[1][n][2]);
ch;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】