断网练习 2
Day 31
2466 Sue 的小球
一遍过捏。
点击查看代码
struct node{
int x,y,v;
bool operator < (const node &t) const {
return x < t.x;
}
};
int n,st,be;
node w[N],temp[N];
lwl dp[N][N][2];
lwl sum[N];
lwl dis(int i,int j) {
return abs(w[i].x - w[j].x);
}
int get(int l,int r) {
return sum[n] - (sum[r] - sum[l - 1]);
}
void mx(lwl &a,lwl b) {
if (a < b) a = b;
}
int main(){
lwl qwq = 0;
n = fr(), st = fr();
for (int i = 1; i <= n; i ++) w[i].x = fr();
for (int i = 1; i <= n; i ++) w[i].y = fr(), qwq += w[i].y;
for (int i = 1; i <= n; i ++) w[i].v = fr();
w[0].x = -inf;
sort(w + 1, w + 1 + n);
for (int i = 1,j = 1; j <= n; i ++, j ++) {
temp[j] = w[i];
if (w[i].x > st && w[i - 1].x < st) {
temp[j] = {st,0,0};
be = j;
j ++;
temp[j] = w[i];
n ++;
} else if (w[i].x == st) be = i;
}
if (!be) {
be = ++ n;
w[n] = {st,0,0};
}
for (int i = 1; i <= n; i ++) {
w[i] = temp[i];
sum[i] = sum[i - 1] + w[i].v;
}
memset(dp,-0x3f,sizeof dp);
dp[0][0][1] = dp[0][0][0] = qwq;
for (int len = 1; len < n; len ++) {
for (int l = 0; l <= len && be - l > 0; l ++) {
int r = len - l;
if (be + r > n) continue;
lwl &t1 = dp[l][r][1], &t0 = dp[l][r][0];
if (l) {
lwl a = get(be - l + 1,be + r);
mx(t0,dp[l - 1][r][0] - dis(be - l,be - l + 1) * a);
mx(t0,dp[l - 1][r][1] - dis(be - l,be + r) * a);
}
if (r) {
lwl a = get(be - l,be + r - 1);
mx(t1,dp[l][r - 1][0] - dis(be - l,be + r) * a);
mx(t1,dp[l][r - 1][1] - dis(be + r,be + r - 1) * a);
}
}
}
lwl ans = -linf;
for (int l = 0; l < n; l ++) {
ans = max(ans,dp[l][n - l - 1][1]);
ans = max(ans,dp[l][n - l - 1][0]);
}
printf("%.3lf",1.0 * ans / 1000);
return 0;
}
3736 字符合并
状态压缩加上区间
然后转移的时候,分成两种情况进行转移(或者说有一种情况需要特殊转移)
普通的转移就是枚举中间的端点(这里的断点需要从右往左转移,不然会重复更新,而且为了不超时,所以是一个区间一个区间一跳的),然后再枚举状态,之后将末尾的
如果说当前区间的长度正好可以压缩的话,那么就把每一个状态都跑一遍,然后试一试这一个区间是当前状态的时候压缩成
这题空间好像卡的还挺紧的()
点击查看代码
const int K = (1 << 8) + 5;
int n,k;
int w[N];
int c[K];
int val[K];
int dp[N][N][K];
void mx(int &a, int b) {
if (a < b) a = b;
}
signed main(){
n = fr(), k = fr();
for (int i = 1; i <= n; i ++) w[i] = fr();
for (int i = 0; i < (1 << k); i ++) {
c[i] = fr(),val[i] = fr();
}
memset(dp,-0x3f,sizeof dp);
for (int i = 1; i <= n; i ++) dp[i][i][w[i]] = 0;
for (int len = 2; len <= n; len ++) {
for (int l = 1; ; l ++) {
int r = l + len - 1;
if (r > n) break;
int t = (len - 1) % (k - 1);
if (!t) t = k - 1;
for (int kk = r - 1; kk >= l; kk -= k - 1) {
for (int h = 0; h < (1 << t); h ++) {
// 1
mx(dp[l][r][(h << 1) | 1],dp[l][kk][h] + dp[kk + 1][r][1]);
// 0
mx(dp[l][r][h << 1],dp[l][kk][h] + dp[kk + 1][r][0]);
}
}
if (t == k - 1) {
lwl t0 = -linf, t1 = -linf;
for (int h = 0; h < (1 << k); h ++) {
if (c[h]) mx(t1,dp[l][r][h] + val[h]);
else mx(t0,dp[l][r][h] + val[h]);
}
dp[l][r][0] = t0;
dp[l][r][1] = t1;
}
}
}
lwl ans = -linf;
for (int i = 0; i < (1 << k); i ++)
mx(ans,dp[1][n][i]);
fw(ans);
return 0;
}
5999 kangaroo
一种之前没有见过的插空
就是把元素不停地放到序列或者什么东西中,然后分成
考虑往其中加入一个元素的时候,有以下三种情况:
-
加入到一段中,因为可以加入到任意一段中,所以转移是:
-
将当前的元素作为新的一段与之前的段放在一起,这个时候这个新的段就是插空放,所以转移是:
-
将当前的元素放在两个段之间,并且合并这两个段,转移:
然后针对这一题来说,其实就相当于求一个排列,让每一个数都比左右两个大或者小,那么联系到这个,我们就可以想到从小到大枚举每一个草丛。然后对应到这三种情况上面去。
-
加入到一段中。因为这个加入的元素是单调递增的,所以直接加的话是没有办法做到比两边大或者比两边小的。
-
作为新的一段,转移方程就和普通的一样(但是因为起点和终点确定了,要判断一下当前有没有遍历过起点和终点,如果遍历过了要减掉对应的空位)
-
合并两个段。因为之前遍历的肯定是比当前元素要小的,所以说这个是可以直接合并的,直接用上面的式子就可以了
然后如果遍历到了起点和终点要特判一下,因为他们不可以随便插入。
点击查看代码
int n,s,t;
lwl dp[N][N];
// 插空dp 前 i 个数 分成 j 段
int main(){
n = fr(), s = fr(), t = fr();
dp[0][0] = 1;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= i; j ++) {
if (s == i || t == i) {
// 合并(另) 新插入
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
} else {
int w = (i > s) + (i > t);
// 合并
dp[i][j] += dp[i - 1][j + 1] * j % mod;
// 新插入
dp[i][j] += dp[i - 1][j - 1] * (j - w) % mod;
dp[i][j] %= mod;
}
}
}
fw(dp[n][1]);
return 0;
}
4563 守卫
这个题目很容易发现的一个性质是如果当前玩耍的区间是
在枚举的时候我们记录一个当前的
初始化的话就把所有长度为
点击查看代码
int n;
int h[N];
int ans;
int dp[N][N];
set<int> s[N];
double get(int i,int j) {
return (double)(h[j] - h[i]) / (j - i);
}
int main(){
n = fr();
for (int i = 1; i <= n; i ++) h[i] = fr();
// r 一定放
for (int r = 1; r <= n; r ++) {
dp[r][r] = 1;
ans ^= 1;
double kmn = dinf;
int p = 0;
for (int l = r - 1; l; l --) {
double k = get(l,r);
if (k < kmn || !p) {
kmn = k;
p = l;
}
dp[l][r] = dp[p + 1][r] + min(dp[l][p - 1], dp[l][p]);
ans ^= dp[l][r];
}
}
fw(ans);
return 0;
}
Day 32
P1879 Corn Fields G
就是玉米田,然后一开始数组开小了,恼。
点击查看代码
int n,m,qwq,siz;
int flag[N];
int dp[N][M];
vector<int> can;
vector<int> h[M];
bool check(int x) {
for (int i = 1; i < m; i ++) {
if ((x >> i) & 1 && (x >> (i - 1)) & 1)
return false;
}
return true;
}
void init() {
for (int i = 0; i <= qwq; i ++) {
if (check(i))
can.push_back(i);
}
siz = can.size();
for (int i = 0; i < siz; i ++) {
int t = can[i];
for (auto j : can) {
if (t & j) continue;
h[i].push_back(j);
}
}
}
int main(){
n = fr(),m = fr();
for (int i = 1; i <= n; i ++) {
for (int j = 0; j < m; j ++) {
int t = fr();
t = 1 - t;
flag[i] += (t << j);
}
}
qwq = (1 << m) - 1;
init();
dp[0][0] = 1;
for (int i = 1; i <= n + 1; i ++) {
for (int j = 0; j < siz; j ++) {
int t = can[j];
if (t & flag[i]) continue;
for (auto tt : h[j]) {
dp[i][t] = (dp[i][t] + dp[i - 1][tt]) % mod;
}
}
}
fw(dp[n + 1][0]);
return 0;
}
POD-Subdivision of Kingdom
一开始断网做的时候以为是什么高端的状压
然后还要预处理一下
点击查看代码
int n,m,qwq;
int w[N];
int cnt[1 << 13];
int ans = -inf,res;
int cntt(int i) {
return cnt[i & qwq] + cnt[i >> 13];
}
void dfs(int u,int u1,int u2,int cnt1,int cnt2,int sum) {
if (cnt1 == n / 2 && cnt2 == n / 2) {
if (sum > ans) {
ans = sum;
res = u1;
}
return ;
}
if (cnt1 < n / 2)
dfs(u + 1,u1 | (1 << u),u2,cnt1 + 1,cnt2,sum + cntt(w[u] & u1));
if (cnt2 < n / 2)
dfs(u + 1,u1,u2 | (1 << u),cnt1,cnt2 + 1,sum + cntt(w[u] & u2));
}
int main(){
n = fr(), m = fr();
qwq = (1 << 13) - 1;
while (m --) {
int a = fr() - 1, b = fr() - 1;
w[a] |= (1 << b);
w[b] |= (1 << a);
}
cnt[1] = 1;
for (int i = 2; i <= qwq; i ++) {
int t = i;
while (t) {
t -= t & -t;
cnt[i] ++;
}
}
dfs(1,1,0,1,0,0);
for (int i = 0; i < n; i ++) {
if (res & (1 << i)) {
fw(i + 1);
kg;
}
}
return 0;
}
3869 宝藏
是一个用了状压表达状态的
点击查看代码
struct node{
int x1,y1,x2,y2;
}h[N];
struct nodee{
int x,y,type;
};
pii S,T;
int n,m,k;
bool flag[N][N];
bool temp[M][N][N];
int ans = inf;
int dis[N][N][M];
vector<int> w[N * N];
int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};
int get(int i,int j) {
return (i - 1) * m + j;
}
pii zb(int w) {
int t = w % m;
if (!t) {
t = m;
w -= m;
}
return {(w / m) + 1,t};
}
void bfs() {
memset(dis,0x3f,sizeof dis);
dis[S.fi][S.se][0] = 0;
queue<pii> q;
q.push({get(S.fi,S.se),0});
while (q.size()) {
auto t = q.front();
q.pop();
int ux = zb(t.fi).fi, uy = zb(t.fi).se;
int type = t.se;
for (int i = 0; i < 4; i ++) {
int vx = ux + dx[i], vy = uy + dy[i];
if (vx > n || !vx || vy > m || !vy)
continue;
int vt = type;
if (w[get(vx,vy)].size()) {
for (auto qwq : w[get(vx,vy)]) {
vt ^= (1 << qwq);
}
}
if (dis[vx][vy][vt] <= inf / 2 || temp[type][vx][vy])
continue;
dis[vx][vy][vt] = dis[ux][uy][type] + 1;
q.push({get(vx,vy),vt});
}
}
}
void init() {
for (int i = 0; i < (1 << k); i ++) {
memcpy(temp[i],flag,sizeof temp[i]);
for (int j = 0; j < k; j ++) {
if ((i >> j) & 1) {
temp[i][h[j].x2][h[j].y2] ^= 1;
}
}
}
}
int main(){
n = fr(), m = fr();
for (int i = 1; i <= n; i ++) {
string s;
cin >> s;
for (int j = 0; j < n; j ++) {
if (s[j] == 'S') S = {i,j + 1};
else if (s[j] == 'T') T = {i,j + 1};
else if (s[j] == '#') flag[i][j + 1] = true;
}
}
k = fr();
for (int i = 0; i < k; i ++) {
int a,b,c,d;
a = fr(), b = fr(), c = fr(), d = fr();
h[i] = {a,b,c,d};
w[get(a,b)].push_back(i);
}
init();
bfs();
for (int i = 0; i < (1 << k); i ++) {
ans = min(ans,dis[T.fi][T.se][i]);
}
fw(ans);
return 0;
}
1171 售货员的难题
就是旅行商问题,一遍过捏。
点击查看代码
int n;
int dis[N][N];
int dp[M][N];
int main(){
n = fr();
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
dis[i][j] = fr();
}
}
memset(dp,0x3f,sizeof dp);
dp[1][0] = 0;
for (int i = 2; i < (1 << n); i ++) {
for (int j = 0; j < n; j ++) {
if ((i >> j) & 1) {
for (int k = 0; k < n; k ++) {
if (i >> k & 1) {
dp[i][k] = min(dp[i][k],dp[i ^ (1 << k)][j] + dis[j][k]);
}
}
}
}
}
int qwq = (1 << n) - 1;
int ans = inf;
for (int i = 1; i < n; i ++)
ans = min(ans,dp[qwq][i] + dis[i][0]);
fw(ans);
return 0;
}
Day 33
4329 Bond
一开始断网的时候
点击查看代码
int n,qwq;
double w[N][N];
double dp[M];
int main(){
n = fr();
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
cin >> w[i][j];
w[i][j] /= 100.0;
}
}
qwq = 1 << n;
dp[0] = 1;
for (int i = 0; i < qwq; i ++) {
int cnt = 0;
for (int j = 0; j < n; j ++) if (i & (1 << j)) cnt ++;
for (int j = 0; j < n; j ++) {
if (i & (1 << j))
dp[i] = max(dp[i], dp[i ^ (1 << j)] * w[j][cnt - 1]);
}
}
printf("%.6lf",100 * dp[qwq - 1]);
return 0;
}
3052 Cows in a Skyscraper
这个题目用了一些贪心,发现自己贪心的东西好像还不是很会,恼。
贪心就是说如果说前面一个还塞得下的话,就尽量往前面一个塞,让这个空余的位置做到最小之后就不管了,直接开一个新的。
然后转移的时候就直接按照普通的状压
点击查看代码
int n, qwq, W;
int w[N];
pii dp[M];
int main(){
n = fr(), W = fr();
for (int i = 0; i < n; i ++) w[i] = fr();
qwq = 1 << n;
for (int i = 0; i < qwq; i ++) {
dp[i].fi = inf;
}
dp[0] = {1,W};
for (int i = 0; i < qwq; i ++) {
for (int j = 0; j < n; j ++) {
if ((i >> j) & 1) continue;
if (w[j] <= dp[i].se && dp[i | (1 << j)].fi >= dp[i].fi) {
dp[i | (1 << j)] = {dp[i].fi,max(dp[i | (1 << j)].se,dp[i].se - w[j])};
} else if (dp[i].se < w[j] && dp[i | (1 << j)].fi > dp[i].fi) {
dp[i | (1 << j)] = {dp[i].fi + 1,max(dp[i | (1 << j)].se,W - w[j])};
}
}
}
fw(dp[qwq - 1].fi);
return 0;
}
ACwing 1065 涂抹果酱
是一个不是二进制的状压,就按照状压的写就可以了,就是不能直接右移和左移。一开始写的时候有一个循环的
点击查看代码
int n,m,k,qwq;
int flag;
int dp[2][mm];
int three[6] = {1,3,9,27,81,243};
vector<int> use,h[mm];
bool check(int i) {
for (int j = 1; j < m; j ++) {
if ((i / three[j - 1]) % 3 == (i / three[j]) % 3)
return false;
}
return true;
}
void init() {
for (int i = 0; i < qwq; i ++) {
if (check(i)) use.push_back(i);
}
for (auto i : use) {
for (auto j : use) {
if (i == j) continue;
bool flag = true;
for (int k = 0; k < m; k ++) {
if ((i / three[k]) % 3 == (j / three[k]) % 3) {
flag = false;
break;
}
}
if (!flag) continue;
h[i].push_back(j);
}
}
}
int main(){
n = fr(), m = fr();
k = fr();
flag = 0;
for (int i = 1; i <= m; i ++) {
flag = fr() - 1 + flag * 3;
}
qwq = three[m];
init();
int u,v;
for (int i = 1; i <= n; i ++) {
u = i & 1, v = 1 - u;
memset(dp[u],0,sizeof dp[u]);
if (i == k) {
if (i == 1) dp[u][flag] = 1;
else
for (auto j : h[flag])
dp[u][flag] = (dp[u][flag] + dp[v][j]) % mod;
continue;
}
for (int j : use) {
for (auto k : h[j]) {
dp[u][j] = (dp[u][j] + dp[v][k]) % mod;
}
if (i == 1) dp[u][j] = 1;
}
}
lwl ans = 0;
for (auto i : use) {
ans = (ans + dp[u][i]) % mod;
}
fw(ans);
return 0;
}
Day 34
5856 Game
这个题目是先把每个数因式分解,然后记录一下每一个质数有的次数,然后用
然后在算答案之前,因为质数最多的次数是
然后因为他的要求是最后消到一样的数,所以一开始找到这些数的
点击查看代码
int n;
int w[N],h[N * 10];
int pre[N * 10];
int zs[20];
vector<int> qwqq;
int flag[N * 10][20];
int dp[1 << 20];
int gcd(int x,int y) {
if (!y) return x;
return gcd(y, x % y);
}
void init() {
for (int i = 2; i <= 1e6; i ++) {
if (!pre[i]) {
pre[i] = i;
qwqq.push_back(i);
}
for (auto j : qwqq) {
if (j > 1e6 / i) break;
pre[j * i] = j;
if (i % j == 0) break;
}
}
memset(dp,0x3f,sizeof dp);
dp[0] = 0;
int en = (1 << 20) - 1;
for (int j = 0; j < 20; j ++) {
dp[1 << j] = 1;
}
for (int t = 3; t <= en; t ++) {
for (int k = 0; k < 20; k ++) {
int t1 = (t & ((1 << k) - 1));
dp[t] = min(dp[t], dp[(t >> (k + 1)) | t1] + 1);
}
}
}
signed main(){
n = fr();
init();
int t = 0;
int maxn = 0;
for (int i = 1; i <= n; i ++) {
w[i] = fr();
if (!t) t = w[i];
else t = gcd(t,w[i]);
}
for (int i = 1; i <= n; i ++) {
maxn = max(w[i],maxn);
while (pre[w[i]] && w[i] != 1) {
int t = pre[w[i]];
int cnt = 0;
while (w[i] % t == 0) {
cnt ++;
w[i] /= t;
}
flag[t][cnt] = true;
}
}
while (pre[t] && t != 1) {
int tt = pre[t];
int cnt = 0;
while (t % tt == 0) {
cnt ++;
t /= tt;
}
h[tt] = cnt;
}
int ans = 0;
for (auto i : qwqq) {
if (i > maxn) break;
int en = 0;
for (int j = 1; j <= 20; j ++) {
if (flag[i][j]) {
en |= (1 << (j - 1));
}
}
if (!en) continue;
int minn = dp[en];
for (int j = 1; j <= h[i]; j ++)
minn = min(minn,dp[en >> j]);
ans += minn;
}
fw(ans);
return 0;
}
3888 拯救莫莉丝
这个题的循环有点多,然后要注意的一点是这一题给的范围是
点击查看代码
int n,m;
int w[N][N];
int sum[N][K];
int dp[N][K][K],f[N][K][K];
int cnt[K];
int main(){
n = fr(), m = fr();
int qwq = (1 << m);
for (int i = 1; i <= n; i ++) {
for (int j = 0; j < m; j ++) {
w[i][j] = fr();
}
for (int j = 0; j < qwq; j ++) {
for (int t = 0; t < m; t ++) {
if ((j >> t) & 1)
sum[i][j] += w[i][t];
}
}
}
cnt[1] = 1;
for (int i = 2; i < qwq; i ++) {
cnt[i] = cnt[i >> 1] + (i & 1);
}
memset(dp,0x3f,sizeof dp);
memset(f,0x3f,sizeof f);
for (int i = 0; i < qwq; i ++) {
dp[1][i][0] = sum[1][i];
f[1][i][0] = cnt[i];
}
for (int i = 2; i <= n + 1; i ++) {
for (int j = 0; j < qwq; j ++) {
// 第 i 行
for (int k = 0; k < qwq; k ++) {
// 第 i - 1 行
for (int t = 0; t < qwq; t ++) {
// 第 i - 2 行
// 保证第 i - 1 行是都有的
if ((((j | k | t) | (k >> 1) | (k << 1)) & (qwq - 1)) == (qwq - 1)) {
if (dp[i][j][k] > sum[i][j] + dp[i - 1][k][t]) {
dp[i][j][k] = sum[i][j] + dp[i - 1][k][t];
f[i][j][k] = f[i - 1][k][t] + cnt[j];
} else if (dp[i][j][k] == sum[i][j] + dp[i - 1][k][t]) {
f[i][j][k] = min(f[i][j][k],f[i - 1][k][t] + cnt[j]);
}
}
}
}
}
}
pii ans = {inf,inf};
for (int i = 0; i < qwq; i ++) {
if (ans.fi > dp[n + 1][0][i]) {
ans = {dp[n + 1][0][i],f[n + 1][0][i]};
} else if (ans.fi == dp[n + 1][0][i]) {
ans.se = min(ans.se,f[n + 1][0][i]);
}
}
fw(ans.se);
kg;
fw(ans.fi);
return 0;
}
Day 35
外太空旅行
对于这个诈骗题我不好做评价,怎么会有题目的题解全部都是随机化的啊!怎么会有题目的标签有随机化的啊!
当个笑话看算了。
点击查看代码
int n;
lwl qwq;
int w[N];
bool flag[N][N];
bool vis[N];
int ans = 1;
int main(){
n = fr();
int a,b;
while (cin >> a >> b) {
flag[a][b] = flag[b][a] = true;
}
int ans = 1;
for (int i = 1; i <= n; i ++) w[i] = i;
for (int i = 0; i <= 92; i ++) {
random_shuffle(w + 1, w + n + 1);
memset(vis,0,sizeof vis);
int cnt = 0;
for (int i = 1; i <= n; i ++) {
if (!vis[w[i]]) {
for (int j = 1; j <= n; j ++) {
if (!flag[w[i]][w[j]]) vis[w[j]] = true;
}
cnt ++;
}
}
ans = max(ans,cnt);
if (ans == n) break;
}
fw(ans);
return 0;
}
4802 短路最
很简单的旅行商问题(),直接水过。
点击查看代码
int n, m;
int dis[N][N];
int dp[N][M];
int main(){
n = fr(), m = fr();
memset(dis,-0x3f,sizeof dp);
while (m -- ){
int a = fr(), b = fr(), w = fr();
dis[a][b] = w;
}
dp[0][1] = 0;
int qwq = (1 << n);
for (int i = 3; i < qwq; i ++) {
for (int j = 1; j < n; j ++) {
if (!((i >> j) & 1)) continue;
for (int k = 0; k < n; k ++) {
if (!((i >> k) & 1) || j == k) continue;
dp[j][i] = max(dp[j][i],dp[k][i ^ (1 << j)] + dis[k][j]);
}
}
}
int ans = -inf;
for (int i = (1 << (n - 1)); i < qwq; i ++) {
ans = max(ans,dp[n - 1][i]);
}
fw(ans);
return 0;
}
Day 36
6962 朋也与光玉
一遍过捏。感觉挺水的()
点击查看代码
int n, m, k;
int w[N];
vector<node> e[14][N];
vector<int> h[N];
lwl dp[N][K];
int main(){
memset(dp,0x3f,sizeof dp);
n = fr(), m = fr(), k = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
dp[i][(1 << w[i])] = 0;
h[w[i]].push_back(i);
}
while (m --) {
int a = fr(), b = fr(), val = fr();
if (w[a] == w[b]) continue;
e[w[b]][a].push_back({b,val});
}
int qwq = (1 << k) - 1;
for (int i = 3; i <= qwq; i ++) {
for (int j = 0; j < k; j ++) {
if (!(i & (1 << j))) continue;
for (auto &u : h[j]) {
for (int t = 0; t < k; t ++) {
if (t == j || !((i >> t) & 1)) continue;
for (auto &it : e[t][u]) {
int v = it.v,w = it.w;
dp[u][i] = min(dp[u][i],dp[v][i ^ (1 << j)] + w);
}
}
}
}
}
lwl ans = linf;
for (int i = 1; i <= n; i ++)
ans = min(ans,dp[i][qwq]);
if (ans >= inf) wj;
else fw(ans);
return 0;
}
3070 Island Travels G
这个题先找出所有连通块,然后求出每两个连通块之间的距离最小值,这个用
在断网的时候写的是用
点击查看代码
struct node{
int x,y;
int type;
};
int n,m;
int idx = 0;
int flag[N][N];
int w[N][N];
int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};
int dis[15][15];
int dp[N][K];
int dist[N][N];
bool vis[N][N];
vector<pii> h[N];
void dfs(int x,int y) {
bool flg = false;
w[x][y] = idx;
for (int i = 0; i < 4; i ++) {
int vx = x + dx[i],vy = y + dy[i];
if (!vx || !vy || vx > n || vy > m) continue;
if (flag[vx][vy] == 1) flg = true;
if (flag[vx][vy] || ~w[vx][vy]) continue;
w[vx][vy] = idx;
dfs(vx,vy);
}
if (flg) h[idx].push_back({x,y});
}
void spfa(int u) {
memset(dist,0x3f,sizeof dist);
queue<pii> q;
for (auto t : h[u]) {
vis[t.fi][t.se] = 1;
dist[t.fi][t.se] = 0;
q.push(t);
}
while (q.size()) {
auto t = q.front();
q.pop();
int x = t.fi,y = t.se;
vis[x][y] = false;
for (int i = 0; i < 4; i ++) {
int vx = x + dx[i],vy = y + dy[i];
if (!vx || !vy || vx > n || vy > m) continue;
if (flag[vx][vy] == inf) continue;
if (dist[vx][vy] > dist[x][y] + flag[vx][vy]) {
dist[vx][vy] = dist[x][y] + flag[vx][vy];
if (!flag[vx][vy])
dis[u][w[vx][vy]] = min(dis[u][w[vx][vy]],dist[vx][vy]);
if (!vis[vx][vy]) {
vis[vx][vy] = true;
q.push({vx,vy});
}
int t = dist[1][2];
}
}
}
}
int main(){
memset(w,-1,sizeof w);
n = fr(), m = fr();
for (int i = 1; i <= n; i ++) {
string s;
cin >> s;
for (int j = 0; j < m; j ++) {
if (s[j] == '.') flag[i][j + 1] = inf;
else if (s[j] == 'S') flag[i][j + 1] = 1;
}
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
if ((!flag[i][j]) && w[i][j] == -1) {
dfs(i,j);
idx ++;
}
}
}
memset(dp,0x3f,sizeof dp);
memset(dis,0x3f,sizeof dis);
for (int i = 0; i < idx; i ++) {
spfa(i);
dp[i][(1 << i)] = 0;
}
int qwq = (1 << idx) - 1;
for (int i = 3; i <= qwq; i ++) {
for (int j = 0; j < idx; j ++) {
if (!((i >> j) & 1)) continue;
for (int k = 0; k < idx; k ++) {
if (!((i >> k) & 1) || k == j) continue;
dp[j][i] = min(dp[j][i],dp[k][i ^ (1 << j)] + dis[k][j]);
}
}
}
int ans = inf;
for (int i = 0; i < idx; i ++) {
ans = min(ans,dp[i][qwq]);
}
fw(ans);
return 0;
}
8280 Photoelectric Effect
这个题是带状压的树形
在做的时候需要预处理一下如果两个状态合并起来可以变成什么颜色,后面转移的时候要用。
然后这个地方在多测清空的时候,不能直接
然后就是在树形
然后对于不是叶子节点的点,就先把他的第一个儿子给考虑了,因为如果只考虑一个子树的话子树的颜色是没有办法影响到当前点的状态的。所以就把所有颜色还有第一个子树的状态都遍历一下然后记录下来。而其它子树就要考虑前面的子树,因为只要有两个子树,那么这个节点的颜色就被决定了(这个就是前面预处理的东西),所以遍历一下当前子树的状态以及这个子树前面的所有子所对应的当前节点的状态,再决定这个节点是什么颜色的。
然后因为这里之前处理的子树的状态是不包括当前子树的根节点的状态的,所以这里要把这个状态给
点击查看代码
int n,k;
int w[K][K];
int col[1 << K][1 << K];
vector<int> e[N];
lwl dp[N][K][1 << K];
lwl tmp[K][1 << K];
void dfs(int u) {
if (!e[u].size()) {
for (int i = 0; i < k; i ++)
dp[u][i][0] = 1;
return ;
}
for (auto v : e[u]) {
dfs(v);
}
int v = e[u][0];
for (int t = 0; t < k; t ++) {
for (int i = 0; i < (1 << k); i ++) {
for (int j = 0; j < k; j ++) {
dp[u][t][i | (1 << j)] = (dp[u][t][i | (1 << j)] + dp[v][j][i]) % mod;
}
}
}
for (int i = 1; i < e[u].size(); i ++) {
int v = e[u][i];
for (int ii = 0; ii < (1 << k); ii ++) {
for (int j = 0; j < k; j ++) {
tmp[j][ii] = dp[u][j][ii];
dp[u][j][ii] = 0;
}
}
for (int j = 1; j < (1 << k); j ++) {
for (int t = 0; t < (1 << k); t ++) {
for (int _ = 0; _ < k; _ ++) {
int uu = (1 << _) | t;
if (col[j][uu] == -1) continue;
dp[u][col[j][uu]][j | uu] = (dp[u][col[j][uu]][j | uu] + dp[v][_][t] * tmp[col[j][uu]][j] % mod) % mod;
}
}
}
}
}
void get(int x,int y) {
col[x][y] = k;
for (int i = 0; i < k; i ++) {
if (!((x >> i) & 1)) continue;
for (int j = 0; j < k; j ++) {
if (!((y >> j) & 1)) continue;
if (col[x][y] == k)
col[x][y] = w[i][j];
if (w[i][j] != col[x][y] || w[j][i] != col[x][y]){
col[x][y] = -1;
return ;
}
}
}
}
int main(){
int T = fr();
while (T --) {
memset(col,0,sizeof col);
n = fr(), k = fr();
for (int i = 0; i < k; i ++) {
for (int j = 0; j < k; j ++) {
w[i][j] = fr() - 1;
}
}
for (int i = 1; i <= n; i ++) {
for (int j = 0; j < k; j ++) {
for (int qwq = 0; qwq < (1 << k); qwq ++) {
dp[i][j][qwq] = 0;
}
}
}
for (int i = 1; i < (1 << k); i ++) {
for (int j = 1; j < (1 << k); j ++) {
get(i,j);
}
}
for (int i = 1; i <= n; i ++) e[i].clear();
for (int i = 2; i <= n; i ++) {
e[fr()].push_back(i);
}
dfs(1);
lwl ans = 0;
for (int i = 0; i < k; i ++) {
for (int j = 0; j < (1 << k); j ++) {
ans = (ans + dp[1][i][j]) % mod;
}
}
fw(ans);
ch;
}
return 0;
}
Day 37
2167 Bill的挑战
一遍过捏。状压
遍历每一位,然后枚举一下这个位置是哪一个字母,然后遍历一遍每个字母,如果说当前位不匹配的话就标记一下,然后把前面的状态遍历一下再转移过来。
点击查看代码
int n, k;
string s[N];
lwl dp[55][1 << N];
// 第几位,只考虑前面的匹配
bool flag[26];
int cnt[1 << N];
void init() {
for (int i = 1; i < (1 << N); i ++)
cnt[i] = cnt[i >> 1] + (i & 1);
}
int main(){
int T = fr();
init();
while (T --) {
n = fr(), k = fr();
int len = 0;
for (int i = 0; i < n; i ++) {
cin >> s[i];
if (!len) len = s[i].length();
bool flag = true;
for (int j = 0; j < len; j ++) {
if (s[i][j] != '?') {
flag = false;
break;
}
}
if (flag) {
i --, k --, n --;
}
}
if (k > n || k < 0) {
wj;
continue;
}
int qwq = (1 << n) - 1;
memset(dp,0,sizeof dp);
dp[0][qwq] = 1;
for (int i = 0; i < len; i ++) {
int t = i + 1;
memset(flag,0,sizeof flag);
for (int kk = 0; kk < 26; kk ++) {
int u = 0;
for (int j = 0; j < n; j ++)
if (s[j][i] - 'a' == kk || s[j][i] == '?')
u |= (1 << j);
for (int o = 0; o <= qwq; o ++)
dp[t][o & u] = (dp[t][o & u] + dp[t - 1][o]) % mod;
}
}
lwl ans = 0;
for (int i = 0; i <= qwq; i ++) {
if (cnt[i] == k)
ans = (ans + dp[len][i]) % mod;
}
fw(ans);
ch;
}
return 0;
}
2157 学校食堂
这个题目是状压 + 线性
然后转移的时候考虑两种情况,一个是从当前位可以转移到下一位,也就是说当前状态自己这个位置所对应的是已经吃过饭了的。
还有一种情况就是说还是这一位,自己内部转移,也就是后面那个
然后因为这里遍历前一个吃饭的人是谁的时候,要遍历前面的人,也就是说这里的
点击查看代码
int n;
pii w[N];
int dp[N][20][K];
// 遍历到第几个 上一个菜是哪一个(相对位置) 前后的状态
int main(){
int T = fr();
while (T --) {
n = fr();
memset(dp,0x3f,sizeof dp);
for (int i = 1; i <= n; i ++) {
w[i].fi = fr();
w[i].se = fr();
}
dp[1][7][0] = 0;
int qwq = (1 << 8) - 1;
for (int i = 1; i <= n; i ++) {
for (int k = 0; k <= qwq; k ++) {
for (int j = -8; j <= 7; j ++) {
int u = j + 8;
if (dp[i][u][k] < inf) {
if (k & 1)
dp[i + 1][u - 1][k >> 1] = min(dp[i + 1][u - 1][k >> 1],dp[i][u][k]);
else {
int limit = inf;
for (int l = 0; l <= 7; l ++) {
if (l + i > limit) break;
if ((k >> l) & 1) continue;
limit = min(limit,w[i + l].se + i + l);
int ww = dp[i][u][k];
if (i + j > 0)
ww += (w[i + j].fi ^ w[i + l].fi);
dp[i][l + 8][k | (1 << l)] = min(dp[i][l + 8][k | (1 << l)],ww);
}
}
}
}
}
}
int ans = inf;
for (int i = 0; i <= 8; i ++) {
ans = min(ans,dp[n + 1][i][0]);
}
fw(ans);
ch;
}
return 0;
}
Day 38
ARC132C Almost Sorted
一遍过捏,和学校食堂很像,还比学校食堂更简单些。
点击查看代码
int n,d;
int w[N];
lwl dp[N][(1 << 11) + 5];
bool flag[N];
int main(){
n = fr(), d = fr();
bool st = false;
for (int i = 1; i <= n; i ++) {
w[i] = fr();
flag[w[i]] = true;
if (w[i] - i > d) {
st = true;
break;
}
}
if (st) {
wj;
return 0;
}
int qwq = (1 << (d * 2 + 1)) - 1;
int h = d * 2 + 1;
dp[0][0] = 1;
int awa = (1 << (d * 2));
for (int i = 1; i <= n; i ++) {
for (int k = 0; k <= qwq; k ++) {
// 上一个数的状态
for (int j = 1; j < h; j ++) {
if ((k >> j) & 1) continue;
int t = i + j - d - 1;
if ((~w[i]) && t != w[i]) continue;
if (t <= 0 || (flag[t] && w[i] != t)) continue;
int u = (k >> 1) | (1 << (j - 1));
dp[i][u] = (dp[i][u] + dp[i - 1][k]) % mod;
}
if (w[i] == i + d || w[i] == -1)
dp[i][(k >> 1) + awa] = (dp[i][(k >> 1) + awa] + dp[i - 1][k]) % mod;
}
}
lwl ans = dp[n][(1 << (d + 1)) - 1];
fw(ans);
return 0;
}
Day 39
6289 Vijestica
状压
然后我们再考虑合并,先看合并两个字符串的时候,定义一个数
然后再进一步,就考虑两个以上的字符串合并的时候,其实就和两个合并差不多的,我们可以先合并其中的一部分字符串和另一部分字符串,然后再把这两个合并的字符串合并在一起就可以了,其实就和两个合并在一起是一样的,也是找一下
点击查看代码
int n;
lwl w[N][N];
int len[N];
lwl dp[K];
int main(){
int n = fr();
for (int i = 0; i < n; i ++) {
string s;
cin >> s;
int lth = s.length();
for (int j = 0; j < lth; j ++)
w[i][s[j] - 'a'] ++;
len[i] = lth;
}
int qwq = (1 << n) - 1;
memset(dp,0x3f,sizeof dp);
for (int i = 0; i < n; i ++)
dp[1 << i] = len[i];
for (int i = 0; i <= qwq; i ++) {
if ((i & -i) == i) continue;
lwl awa = 0;
for (int t = 0; t < 26; t ++) {
lwl minn = linf;
for (int j = 0; j < n; j ++) {
if ((i >> j) & 1)
minn = min(minn,w[j][t]);
}
awa += minn;
}
for (int j = i & (i - 1); j; j = i & (j - 1)) {
dp[i] = min(dp[i],dp[j] + dp[i ^ j] - awa);
}
}
fw(dp[qwq] + 1);
return 0;
}
9029 Cokolade
这个朴素的
正解的话,先排一个序,很明显这个选取买哪些的时候,肯定是前面选一段后面选一段,所以需要考虑的就是该怎么选。
首先要找的肯定就是这个价格的分界线在哪里,这个地方用一个
找到之后就二分一下该怎么分,这里二分的是当当前的点(前面一半)是所有选取的数里面的最大值的时候,最多可以选多少个巧克力,然后当他
点击查看代码
int n,T;
int w[N];
lwl k,m;
lwl sum[N];
lwl get(int l,int r) {
return sum[r] - sum[l - 1];
}
int check(lwl x) {
int l = 0, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (w[mid] < x) l = mid + 1;
else r = mid - 1;
}
return n - l + 1;
}
signed main(){
n = fr(), T = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
sort(w + 1,w + 1 + n);
for (int i = 1; i <= n; i ++)
sum[i] = sum[i - 1] + w[i];
while (T --) {
k = fr(), m = fr();
int l = 0, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (w[mid] <= k) l = mid + 1;
else r = mid - 1;
}
lwl pos = r;
l = 0, r = min(m,pos);
int res = l;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(2 * k - w[mid]) + mid <= m) {
l = mid + 1;
res = mid;
}
else r = mid - 1;
}
lwl ans = sum[res] + 2 * k * (m - res) - get(n - m + res + 1,n);
fw(ans);
ch;
}
return 0;
}
Day 40
2607 骑士
这个是基环树加上
然后因为这里要标记边,所以存边的
点击查看代码
struct node {
int v;
int idx;
};
int n;
lwl dp[N][2];
int w[N];
vector<node> e[N];
int s1,s2;
int t;
bool flag[N];
void dfs(int u,int fa) {
flag[u] = true;
for (auto it : e[u]) {
int v = it.v;
if (v == fa) continue;
if (flag[v]) {
s1 = u,s2 = v;
t = it.idx;
continue;
}
dfs(v,u);
}
}
void dfs1(int u,int fa) {
dp[u][0] = 0;
dp[u][1] = w[u];
for (auto it : e[u]) {
if (it.idx == t) continue;
int v = it.v;
if (v == fa) continue;
dfs1(v,u);
dp[u][1] = dp[u][1] + dp[v][0];
dp[u][0] = dp[u][0] + max(dp[v][0],dp[v][1]);
}
}
int main(){
n = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
int a = fr();
e[a].push_back({i,i});
e[i].push_back({a,i});
}
lwl ans = 0;
for (int i = 1; i <= n; i ++) {
if (!flag[i]) {
dfs(i,0);
lwl res;
dfs1(s1,0);
res = dp[s1][0];
dfs1(s2,0);
res = max(res,dp[s2][0]);
ans += res;
}
}
fw(ans);
ch;
return 0;
}
5049 旅行 加强版
这个题目的普通版就是找出环,然后把环上每一条边都断一遍然后跑一遍
加强之后原来朴素的
这个贪心的核心就是:当你的前一个还有儿子没走的祖先的最小的儿子比你自己的儿子(在环上的并且其它儿子都访问完了或者没有没有其它儿子)小的时候就反悔。然后记录一下有没有反悔过就可以啦!
注意:这个地方因为在里面要把没有用过的点从小到大排序,如果先在外面排序然后里面用一个queue的话是会爆空间的,在里面用priority_queue就不会爆(虽然不知道为什么,但是实测是这样的)
然后这个找环的改了好几版,这个是标记点的,然后在这个代码里面的是标记点的写法,还有一种标记边的时候的写法也放在这里了:
这里是标记边的写法
bool find_ring(int u,int fa) {
flag[u] = true;
for (auto it : e[u]) {
int v = it.v;
if (v == fa) continue;
if (flag[v]) {
s1 = u, s2 = v;
ring.push_back(it.idx);
return true;
}
if (find_ring(v,u)) {
ring.push_back(it.idx);
if (u != s2) return true;
return false;
}
}
return false;
}
题目的完整(?)代码
int n, m;
int s1,s2;
bool type = false;
vector<int> e[N];
bool ring[N], flag[N];
int ans[N];
bool vis = false;
bool find_ring(int u,int fa) {
flag[u] = true;
for (auto v : e[u]) {
if (v == fa) continue;
if (flag[v]) {
s1 = u, s2 = v;
ring[v] = ring[u] = true;
return true;
}
if (find_ring(v,u)) {
bool st = false;
if (!(ring[u] && ring[v])) {
st = true;
}
ring[v] = ring[u] = true;
return st;
}
}
return false;
}
void solve_tree(int u) {
flag[u] = true;
fw(u),kg;
for (auto v : e[u]) {
if (!flag[v])
solve_tree(v);
}
}
void solve_ring(int u,int fa,int back,int &cnt) {
// 反悔
if (flag[u]) return ;
flag[u] = true;
ans[++ cnt] = u;
priority_queue<int,vector<int>,greater<int> > q;
for (auto v : e[u]) {
if (flag[v] || v == fa) continue;
q.push(v);
}
while (q.size()) {
int v = q.top();
q.pop();
if ((!vis) && ring[v] && v > back && q.empty()) {
vis = true;
return ;
}
int ne = back;
if (q.size() && ring[u]) ne = q.top();
solve_ring(v,u,ne,cnt);
}
}
int main(){
n = fr(), m = fr();
if (m == n) type = true;
while (m --) {
int a = fr(), b = fr();
e[a].push_back(b);
e[b].push_back(a);
}
if (!type) {
for (int i = 1; i <= n; i ++)
sort(e[i].begin(), e[i].end());
solve_tree(1);
return 0;
}
find_ring(1,0);
int cnt = 0;
memset(flag,0,sizeof flag);
solve_ring(1,0,inf,cnt);
for (int i = 1; i <= n; i ++) {
fw(ans[i]);
kg;
}
return 0;
}
Day 41
P5672 Crystal Balls
应该算模拟吧,但是因为这里的模数很大,所以要用龟速乘。一开始不知道为什么可能常数有点大,所以
点击查看代码
lwl n, mod;
lwl w[N];
bool col[N];
void gsc(lwl &a,lwl b) {
lwl ans = 0;
while (b) {
if (b & 1) ans = (ans + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
a = ans;
}
int main(){
n = fr(), mod = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr() % mod;
}
string s;
cin >> s;
for (int i = 0; i < n; i ++) {
if (s[i] == 'P') col[i + 1] = 1;
}
int l = 1, r = n;
bool type = true; // 现在是否从后往前
bool purple = 0;
lwl ans = 0;
int cnt = 0;
while (l < r) {
int cv,cu;
cnt ++;
if (type) {
cu = col[l] ^ purple;
ans = (ans + w[l]) % mod;
l ++;
cv = col[l] ^ purple;
if (cu == cv) {
gsc(w[l],w[l - 1]);
} else if (cu && cv == 0 && (cnt & 1)) {
purple ^= 1;
} else {
type ^= 1;
}
} else {
cu = col[r] ^ purple;
ans = (ans + w[r]) % mod;
r --;
cv = col[r] ^ purple;
if (cu == cv) {
gsc(w[r],w[r + 1]);
} else if (cu && cv == 0 && (cnt & 1)) {
purple ^= 1;
} else {
type ^= 1;
}
}
}
ans = (ans + w[l]) % mod;
fw(ans);
return 0;
}
Day 42
P6381 Odyssey
这个题目很明显是要用拓扑排序,但是我一开始断网的时候想的是标记每一条边,这就导致超时了,但是也有
代码
struct node {
int v,w,l;
int idx;
};
int n,m,k;
int d[N],dd[N];
vector<node> e[N];
unordered_map<lwl,bool> h;
pii w[N];
lwl dis[N];
void init() {
h[0] = 1;
for (int i = 1; i <= 1e5; i ++) {
lwl t = 1;
for (int j = 1; j <= k; j ++) {
t = t * i;
if (t > 1e10) break;
}
if (t <= 1e10) h[t] = true;
else break;
}
}
int main() {
n = fr(), m = fr(), k = fr();
if (k != 1) init();
while (m --) {
int a = fr(), b = fr(), ww = fr(), l = fr();
e[a].push_back({b,ww,l,m});
w[m] = {b,ww};
d[b] ++;
}
int hh = 0, tt = 0;
for (int i = 1; i <= n; i ++) {
if (!d[i]) {
for (auto &it : e[i]) {
q.push(it.idx);
dis[it.idx] = it.l;
}
}
for (auto &it : e[i]) {
dd[it.idx] = d[i];
}
}
lwl ans = 0;
while (q.size()) {
int idx = q.front();
q.pop();
pii t = w[idx];
lwl dist = dis[idx];
ans = max(ans,dist);
for (auto &it : e[t.fi]) {
dd[it.idx] --;
if (k == 1 || h[it.w * t.se]) {
dis[it.idx] = max(dis[it.idx], dist + it.l);
} else {
dis[it.idx] = max(dis[it.idx],(lwl)it.l);
}
if (!dd[it.idx])
q.push(it.idx);
}
}
fw(ans);
return 0;
}
后面开了网之后,经过 xyzfrozen 的指点以及代码(?),搞懂了这个题。因为他要求最后求出来两条边是一个
但是这个是按照点来拓扑,但是记录
点击查看代码
struct node {
int v,w,l;
};
int n,m,k;
int d[N];
vector<node> e[N];
vector<int> zs;
int match[N];
map<pii,lwl> dis;
int pre[N];
void init() {
for (int i = 2; i <= 1e5; i ++) {
if (!pre[i]) {
pre[i] = i;
zs.push_back(i);
}
for (auto j : zs) {
if (j > 1e5 / i) break;
pre[i * j] = j;
if (i % j == 0) break;
}
}
}
int get(int t) {
unordered_map<int,int> qwq;
while (t > 1) {
qwq[pre[t]] ++;
t /= pre[t];
}
int a = 1, b = 1;
for (auto it : qwq) {
a = a * pow(it.fi,(it.se % k));
b = b * pow(it.fi,(k - it.se % k) % k);
}
match[a] = b;
return a;
}
int main() {
n = fr(), m = fr(), k = fr();
init();
while (m --) {
int a = fr(), b = fr(), ww = fr(), l = fr();
ww = get(ww);
e[a].push_back({b,ww,l});
d[b] ++;
}
queue<int> q;
for (int i = 1; i <= n; i ++) {
if (!d[i]) {
q.push(i);
}
}
lwl ans = 0;
while (q.size()) {
auto u = q.front();
q.pop();
for (auto it : e[u]) {
d[it.v] --;
dis[{it.v,it.w}] = max(dis[{it.v,it.w}],dis[{u,match[it.w]}] + it.l);
ans = max(ans,dis[{it.v,it.w}]);
if (!d[it.v]) q.push(it.v);
}
}
fw(ans);
return 0;
}
P4999 烦人的数学作业
这个题目就是数位
一遍过捏。
点击查看代码
void init() {
ten[0] = 1;
for (int i = 1; i <= 19; i ++) {
w[i] = (w[i - 1] * 10 + 45 * ten[i - 1]) % mod;
ten[i] = 10 * ten[i - 1] % mod;
}
}
pii dfs(int u,bool limit) {
if (u == 0) return{1,0};
if (!limit) return {ten[u],w[u]};
lwl ans = 0;
int cnt = 0;
int up = 9;
if (limit) up = a[u];
for (int i = 0; i <= up; i ++) {
pii t = dfs(u - 1,up == i && limit);
ans += i * t.fi + t.se;
cnt += t.fi;
ans %= mod;
cnt %= mod;
}
return {cnt,ans};
}
lwl solve(lwl t) {
if (!t) return 0;
a.clear();
a.push_back(0);
while (t) {
a.push_back(t % 10);
t /= 10;
}
int n = a.size() - 1;
return dfs(n,1).se;
}
int main() {
int T = fr();
init();
while (T --) {
lwl l = fr(),r = fr();
lwl ans = solve(r) - solve(l - 1);
fw((ans + mod + mod) % mod);
ch;
}
return 0;
}
P5585 Doing Homework
这个题其实感觉还是比较简单的,但是由于我一开始没有看见空间限制,所以
下次一定好好看空间限制。
大概做法就是,先按照
点击查看代码
struct node {
int x,w,t;
bool operator < (const node tt) {
return t > tt.t;
}
};
int main() {
x = fr(), W = fr();
n = fr();
for (int i = 1; i <= n; i ++) {
w[i].x = fr(),w[i].w = fr(),w[i].t = fr();
}
sort(w + 1, w + 1 + n);
memset(t,0x3f,sizeof t);
memset(dp,0x3f,sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = w[i].w; j <= 2 * W; j ++) {
dp[j] = min(dp[j],dp[j - w[i].w] + w[i].x);
}
for (int j = W; j <= 2 * W; j ++) {
t[i] = min(t[i],dp[j]);
}
}
for (int i = 1; i <= n; i ++) {
}
int u = n;
int ans = 0;
int ne = 0;
while (x > 0 && u >= 1) {
lwl tt = min(x / t[u],(lwl)w[u].t - ne);
x -= t[u] * tt;
ans += tt;
if (tt != w[u].t - ne) break;
ne = w[u].t;
u --;
}
fw(ans),kg;
fw(x);
return 0;
}
P7291 人赢 加强版
贪心,
贪心的话考虑从前往后遍历,在每一次遍历到一个点的时候,就先取一下
证明的话就稍微感性理解一下算了,贪心哪需要证明啊(不是)。
点击查看代码
int n;
int w[N];
int main() {
n = fr();
lwl ans = 0;
for (int i = 1; i <= n; i ++) {
w[i] = fr();
ans = max(ans,(lwl)w[i] * i);
}
int j = n;
for (int i = n - 1; i; i --) {
ans = max(ans,(lwl)min(w[i],w[j]) * (i + j));
if (w[j] < w[i]) j = i;
}
fw(ans);
return 0;
}
Day 43
7355 抽奖
这个题目其实就是一个推结论的题目,但是有点前置知识:
等比序列的求和公式:
这里的
指的是首项, 是等比序列的项数, 是等比的比
注:这个公式不能处理等比是 1 的情况
有了这个公式之后,就可以推式子了。推式子的过程就省去了,这里就记录一下大概思路:考虑将多少兑换卷抽成体验卡,然后再考虑要将哪一个换成永久道具,然后就可以推出下面这个式子:
然后按照这个式子直接计算就可以了,公式里面的除法要转化成逆元。
点击查看代码
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);
}
lwl sum(lwl x,lwl y) {
if (!x) return 1;
if (x == 1) return y;
return 1ll * (ksm(x,y) - 1) % mod * ny(x - 1) % mod;
}
int main() {
int T = fr();
while (T --) {
n = fr(), m = fr();
lwl ans = (n + 1) * sum(n,m + 1) % mod;
ans = (ans - n * sum(n - 1,m + 1) % mod) % mod;
fw((ans + mod) % mod);
ch;
}
return 0;
}
P8251 丹钓战
倍增 + 模拟
模拟之后可以知道,在
这样就可以发现,每一个二元组,要么被一个成功的二元组弹出去,要么留到末尾。因此就可以记录一下每个二元组会被哪一个二元组弹出去,之后再倍增做就可以了。
点击查看代码
int n;
pii w[N];
int ne[N][25];
int main(){
n = fr();
int T = fr();
for (int i = 1; i <= n; i ++) w[i].fi = fr();
for (int i = 1; i <= n; i ++) w[i].se = fr();
stack<int> s;
for (int i = 1; i <= n; i ++) {
while (s.size() && (w[s.top()].fi == w[i].fi || w[s.top()].se <= w[i].se)) {
ne[s.top()][0] = i;
s.pop();
}
s.push(i);
}
int maxn = log2(n) + 1;
for (int i = 1; i <= maxn; i ++) {
for (int j = 1; j <= n; j ++) {
ne[j][i] = ne[ne[j][i - 1]][i - 1];
}
}
while (T --) {
int l = fr(), r = fr();
int ans = 1;
for (int i = maxn; i >= 0; i --) {
if (ne[l][i] <= r && ne[l][i]) {
l = ne[l][i];
ans += (1 << i);
}
}
fw(ans);
ch;
}
return 0;
}
P9437 一棵树
换根
记录一下次数和总和,然后还要对于每一个数来说,求一下如果要和其它的点拼到一起的话,要乘上多少,这个其实也就是
然后开
点击查看代码
int n, root;
int siz[N], fa[N];
pii w[N];
lwl dp[N][3];// 算的次数
vector<int> e[N];
lwl ten[11] = {1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,10000000000};
int d[N];
lwl get(lwl a) {
if (!a) return 10;
int cnt = 0;
while (a) {
cnt ++;
a /= 10;
}
return ten[cnt];
}
void dfs(int u,int father) {
siz[u] = 1, dp[u][0] = 1;
fa[u] = father;
for (auto v : e[u]) {
if (v == father) continue;
dfs(v,u);
siz[u] += siz[v];
dp[u][0] = dp[u][0] + w[v].se * dp[v][0] % mod;
// 以当前点为终点
dp[u][0] %= mod;
}
dp[u][1] = dp[u][0];
}
void get(int u,int father) {
for (auto v : e[u]) {
if (v == father) continue;
dp[u][2] += siz[v] * (dp[u][1] - dp[v][0] * w[v].se % mod) % mod;
dp[u][2] %= mod;
dp[v][1] = (dp[v][1] + (dp[u][1] - dp[v][0] * w[v].se % mod) * w[u].se % mod) % mod;
}
dp[u][2] += (n - siz[u]) * dp[u][0] % mod;
dp[u][2] %= mod;
for (auto v : e[u]) {
if (v == father) continue;
get(v,u);
}
}
signed main() {
n = fr();
ten[10] %= mod;
for (int i = 1; i <= n; i ++) {
lwl a = fr();
w[i] = {a,get(a)};
}
for (int i = 1; i < n; i ++) {
int t = fr();
e[t].push_back(i + 1);
e[i + 1].push_back(t);
d[t] ++, d[i + 1] ++;
}
root = 1;
dfs(root,0);
get(root,0);
lwl ans = 0;
for (int i = 1; i <= n; i ++) {
ans = (ans + w[i].fi * (dp[i][1] + dp[i][2])) % mod;
}
fw((ans + mod) % mod);
return 0;
}
Keep Connect
因为这个地方只有两行,而且
状态定义的话就是
点击查看代码
int n,mod;
lwl dp[N][N][2];
// 前i列 删去j条边 是否连通
int main(){
n = fr(), mod = fr();
dp[1][0][1] = dp[1][1][0] = true;
for (int i = 1; i <= n; i ++) {
for (int j = 0; j < n; j ++) {
dp[i + 1][j][1] += dp[i][j][1];
dp[i + 1][j][1] %= mod;
dp[i + 1][j][1] += dp[i][j][0];
dp[i + 1][j][1] %= mod;
dp[i + 1][j + 1][0] += dp[i][j][0];
dp[i + 1][j + 1][0] %= mod;
dp[i + 1][j + 2][0] += 2 * dp[i][j][1];
dp[i + 1][j + 2][0] %= mod;
dp[i + 1][j + 1][1] += 3 * dp[i][j][1];
dp[i + 1][j + 1][1] %= mod;
}
}
for (int i = 1; i < n; i ++) {
fw(dp[n][i][1]);
kg;
}
return 0;
}
Day 44
P3871 中位数
一遍过捏
对顶堆模拟,保证小根堆里面的元素数量大于等于二分之一的总元素数量
点击查看代码
int main() {
int n = fr();
priority_queue<int,vector<int>,greater<int> > da;
priority_queue<int> xiao;
int siz = n;
for (int i = 1; i <= n; i ++) {
xiao.push(fr());
}
for (int i = 1; i <= (siz >> 1); i ++) {
da.push(xiao.top());
xiao.pop();
}
int m = fr();
while (m --) {
string op;
cin >> op;
if (op[0] == 'a') {
siz ++;
int a = fr();
if (a <= xiao.top()) xiao.push(a);
else da.push(a);
if (xiao.size() > da.size() + 1) {
da.push(xiao.top());
xiao.pop();
}
if (xiao.size() < da.size()) {
xiao.push(da.top());
da.pop();
}
} else {
fw(xiao.top());
ch;
}
}
return 0;
}
P4071 排列计数
一遍过捏
错排吧应该是,虽然当时做的时候没有想到错排的结论,于是就打表看了一下错排的数量,然后找规律推出来一个这样的式子
然后就是选出
点击查看代码
lwl ny(lwl a) {
lwl ans = 1, k = mod - 2;
while (k) {
if (k & 1) ans = ans * a % mod;
a = a * a % mod;
k >>= 1;
}
return ans;
}
void init() {
w[0] = 1;
for (int i = 1; i <= N - 5; i ++) {
if (i & 1) w[i] = (w[i - 1] * i - 1) % mod;
else w[i] = (w[i - 1] * i + 1) % mod;
}
f[0] = nf[0] = 1;
for (int i = 1; i <= N - 5; i ++) {
f[i] = f[i - 1] * i % mod;
}
nf[N - 5] = ny(f[N - 5]);
for (int i = N - 6; i >= 0; i --) {
nf[i] = nf[i + 1] * (i + 1) % mod;
}
}
lwl C(int a,int b) {
if (a == b || !b) return 1;
return f[a] * nf[a - b] % mod * nf[b] % mod;
}
int main() {
init();
int T = fr();
while (T --) {
n = fr(), m = fr();
fw(C(n,m) * w[n - m] % mod);
ch;
}
return 0;
}
P4588 数学计算
一遍过捏。
线段树。因为这一题的模数不一定是质数,所以说不能用逆元直接算,但是可以用线段树维护,在他进行某个操作的时候就把那个位置的值改为要乘的值,删除某个值的时候就是把那个位置的值改为
点击查看代码
int Q,mod;
void push_up(int idx) {
tr[idx].mul = tr[il].mul * tr[ir].mul % mod;
}
void build(int l,int r,int idx) {
L = l,R = r;
tr[idx].mul = 1;
if (L == R) return ;
int mid = (l + r) >> 1;
build(l,mid,il);
build(mid + 1,r,ir);
}
void modify(int idx,int pos,int k) {
if (L == R) {
tr[idx].mul = k;
return ;
}
int mid = (L + R) >> 1;
if (mid >= pos) modify(il,pos,k);
else modify(ir,pos,k);
push_up(idx);
}
int main() {
int T = fr();
while (T --) {
Q = fr(), mod = fr();
int cnt = 0;
build(1,Q,1);
while (Q --) {
cnt ++;
int op = fr();
if (op & 1) {
lwl k = fr();
modify(1,cnt,k);
} else {
lwl pos = fr();
modify(1,pos,1);
}
fw(tr[1].mul);
ch;
}
}
return 0;
}
P8955 Card Tricks
一遍过捏。
这个题目是比赛里面的一个题目,当时还参加了这个比赛,但是当时这一题好像打的是暴力。后面补题的时候也补了,所以这次会做。我竟然还记得这一题,我哭死。
像是扫描线的写法,好像叫线段树二分。像差分一样存储一下操作,然后遍历每一个数,在遍历这个数的时候,先将之前存储的在这个数这里的操作操作一下,然后线段树二分出最小的操作使得这个时候的值大于
点击查看代码
void push_up(int idx) {
tr[idx].w = tr[il].w | tr[ir].w;
}
void build(int l,int r,int idx) {
L = l, R = r;
if (l == r) return ;
int mid = (l + r) >> 1;
build(l,mid,il);
build(mid + 1,r,ir);
}
void modify(int idx,int pos,int val) {
if (L == R) {
tr[idx].w = val;
return ;
}
int mid = (L + R) >> 1;
if (mid >= pos) modify(il,pos,val);
else modify(ir,pos,val);
push_up(idx);
}
int query(int idx,int val) {
if ((tr[idx].w | val) <= p) return -1;
if (L == R) {
if ((tr[idx].w | val) > p) return L;
return -1;
}
if ((tr[il].w | val) > p) return query(il,val);
else return query(ir,val | tr[il].w);
}
int main() {
freopen("qwq.in","r",stdin);
n = fr(), Q = fr(),p = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
for (int i = 1; i <= Q; i ++) {
int l = fr(), r = fr(), v = fr();
q[l].push_back({i,v});
q[r + 1].push_back({i,0});
}
build(1,Q,1);
for (int i = 1; i <= n; i ++) {
for (auto &it : q[i]) {
modify(1,it.fi,it.se);
}
fw(query(1,w[i]));
kg;
}
return 0;
}
Day 45
P7795 PROSJEK
一开始写的时候以为这一题是关于
点击查看代码
double check(double x) {
for (int i = 1; i <= n; i ++) {
sum[i] = sum[i - 1] + w[i] * 1.0 - x;
}
double t = 0;
for (int i = k; i <= n; i ++) {
if (sum[i] > t) return true;
t = min(t, sum[i - k + 1]);
}
return false;
}
int main() {
freopen("qwq.in","r",stdin);
n = fr(), k = fr();
for (int i = 1; i <= n; i ++) {
w[i] = fr();
}
double l = 1, r = 1e6;
while (r - l > eps) {
double mid = (r + l) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.6lf",l);
return 0;
}
P7150 Stuck in a Rut S
看题解好像都是用拓扑写的,但是我直接贪心(应该算吧),就是把所有往下走的按照
本来可以一百的,但是因为有一句话忘记删了导致我的主函数跑了
点击查看代码
struct node {
bool type;
int x,y;
int id;
bool operator < (const node t) const {
if (type == t.type) {
if (type) return x < t.x;
return y < t.y;
}
return type < t.type;
}
};
bool cmp(int a,int b) {
return w[a].id < w[b].id;
}
int main() {
n = fr();
string s;
int cnt = 0;
for (int i = 1; i <= n; i ++) {
cin >> s;
if (s[0] == 'N') w[i].type = 1;
else cnt ++;
w[i].x = fr(), w[i].y = fr();
w[i].id = i;
}
// 0 => x ++
// 1 => y ++
sort(w + 1, w + 1 + n);
for (int i = 1; i <= n; i ++) {
id[w[i].id] = i;
}
for (int i = 1; i <= cnt; i ++) {
// i => x ++ j => y ++
for (int j = cnt + 1; j <= n; j ++) {
if (flag[j] || w[i].y < w[j].y || w[j].x < w[i].x)
continue;
int t1 = w[j].x - w[i].x, t2 = w[i].y - w[j].y;
if (t1 > t2) {
flag[i] = true;
ans[j] += ans[i] + 1;
break;
} else if (t1 < t2) {
flag[j] = true;
ans[i] += ans[j] + 1;
}
}
}
for (int i = 1; i <= n; i ++) {
fw(ans[id[i]]);
ch;
}
return 0;
}
Cheap Robot
感觉是一个图论杂烩题,题目要求求电量最小值,我们就考虑两个点
这个地方求每个点到充电桩的最小距离可以建一个虚拟源点容纳后跑
然后很明显,从
询问的时候直接求一个
点击查看代码
struct node {
int v;
lwl w;
};
struct Node {
int a,b;
lwl w;
bool operator < (const Node t) const {
return w < t.w;
}
}E[N * 3];
int n, m, K, Q;
int idx;
vector<node> edge[N];
vector<int> e[N];
lwl dis[N];
lwl h[N],w[N];
bool flag[N];
int fa[N][30], de[N];
void dij(int st) {
memset(flag,0,sizeof flag);
priority_queue<pii,vector<pii>,greater<pii> > q;
dis[st] = 0;
q.push({dis[st],st});
while (q.size()) {
auto u = q.top().se;
q.pop();
if (flag[u]) continue;
flag[u] = true;
for (auto it : edge[u]) {
int v = it.v;
lwl w = it.w;
if (flag[v]) continue;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({dis[v],v});
}
}
}
}
int find(int x) {
if (x != h[x]) h[x] = find(h[x]);
return h[x];
}
void kruskal() {
for (int i = 1; i <= n * 2; i ++) h[i] = i;
sort(E + 1, E + 1 + m);
idx = n;
for (int i = 1; i <= m; i ++) {
int a = E[i].a, b = E[i].b;
lwl W = E[i].w;
int ha = find(a), hb = find(b);
if (ha != hb) {
w[++ idx] = W;
h[ha] = idx, h[hb] = idx;
e[idx].push_back(ha);
e[idx].push_back(hb);
}
}
}
void dfs(int u,int p) {
fa[u][0] = p;
de[u] = de[p] + 1;
for (int k = 1; k <= 25; k ++)
fa[u][k] = fa[fa[u][k - 1]][k - 1];
for (auto v : e[u]) {
if (v == p) continue;
dfs(v,u);
}
}
int LCA(int x,int y) {
if (de[x] < de[y]) swap(x,y);
int dh = de[x] - de[y];
for (int k = 25; k >= 0; k --) {
if (dh & (1 << k)) {
x = fa[x][k];
}
}
if (x == y) return x;
for (int k = 25; k >= 0; k --) {
if (fa[x][k] != fa[y][k]) {
x = fa[x][k];
y = fa[y][k];
}
}
return fa[x][0];
}
int main() {
memset(dis,0x3f,sizeof dis);
n = fr(), m = fr(), K = fr(), Q = fr();
for (int i = 1; i <= m; i ++) {
int a = fr(), b = fr(), w = fr();
edge[a].push_back({b,w});
edge[b].push_back({a,w});
E[i] = {a,b,w};
}
for (int i = 1; i <= K; i ++) {
edge[0].push_back({i,0});
}
dij(0);
for (int i = 1; i <= m; i ++) {
E[i].w += dis[E[i].a] + dis[E[i].b];
}
kruskal();
dfs(idx,0);
while (Q --) {
int a = fr(), b = fr();
int t = LCA(a,b);
fw(w[t]);
ch;
}
return 0;
}
Day 46
P4343 自动刷题机
直接二分,因为这个
点击查看代码
lwl check(lwl t) {
lwl u = 0, cnt = 0;
for (int i = 1; i <= l; i ++) {
u = max(u + w[i],0ll);
if (u >= t) cnt ++, u = 0;
}
return cnt;
}
int main() {
lwl sum = 0;
l = fr(), k = fr();
for (int i = 1; i <= l; i ++) {
w[i] = fr();
sum += max(w[i],0);
}
lwl l = 1, r = sum;
lwl minn = sum, maxn = 0;
while (l <= r) {
lwl mid = (l + r) >> 1;
if (check(mid) <= k) minn = mid, r = mid - 1;
else l = mid + 1;
}
l = 1, r = sum;
while (l <= r) {
lwl mid = (l + r) >> 1;
if (check(mid) >= k) maxn = mid, l = mid + 1;
else r = mid - 1;
}
if (maxn < minn) wj;
else {
fw(minn),kg;
fw(maxn),ch;
}
return 0;
}
P7961 数列
这个题目一开始做的时候想对了,但是做的过程非常崎岖。首先是一开始看错了,看成等于
用一个
于是乎就可以得到转移方程式:
然后当
这个可以在上一位的时候就提前计算出来,最后统计答案的时候不要忘记算上
点击查看代码
int n,K,m;
lwl w[M];
lwl C[N][N];
// 到哪个 用了几位 这一个放了几个 几个1
// 上一位多少
lwl dp[2][N][N][N][N];
lwl sum[2][N][N][N];
// 权值和
int cnt(int a) {
int count = 0;
while (a) {
count ++;
a -= (a & - a);
}
return count;
}
void init() {
C[0][0] = 1;
for (int i = 1; i <= n; i ++) C[i][0] = 1;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= i; j ++) {
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
}
signed main() {
n = fr(), m = fr(), K = fr();
init();
for (int i = 1; i <= m + 1; i ++) w[i] = fr();
sum[0][0][0][0] = 1;
for (int i = 1; i <= m + 1; i ++) {
int u = i & 1, v = 1 - u;
for (int j = 0; j <= n; j ++) {
for (int t = 0; t <= j; t ++) {
for (int p = 0; p <= j; p ++) {
for (int k = 0; k <= K; k ++) {
if (!t) {
dp[u][j][t][k + (p & 1)][p] = (sum[v][j][k][p << 1] + sum[v][j][k][(p << 1) | 1]) % mod;
continue;
}
dp[u][j][t][k + ((p + t) & 1)][p + t] = dp[u][j - 1][t - 1][k + ((p + t - 1) & 1)][p + t - 1] * w[i] % mod;
}
}
}
}
memset(sum[u],0,sizeof sum[u]);
for (int j = 0; j <= n; j ++) {
for (int t = 0; t <= j; t ++) {
for (int p = 0; p <= n; p ++) {
for (int k = 0; k <= K; k ++) {
sum[u][j][k][p] = (sum[u][j][k][p] + dp[u][j][t][k][p] * C[n - (j - t)][t] % mod) % mod;
}
}
}
}
}
lwl ans = 0;
for (int p = 0; p <= n; p ++) {
for (int k = 0; k <= K; k ++) {
if (k + cnt(p >> 1) <= K)
ans = (ans + sum[(m + 1) & 1][n][k][p]) % mod;
}
}
fw(ans);
return 0;
}
P2331 最大子矩阵
因为这一题的数据范围很小,所以可以
因为
两列的时候,就枚举两列分别用到了哪里,到现在为止有多少个矩阵。那么对于左边这列在
这个的意思就是单列新开一个矩阵
点击查看代码
int n, m, k;
int w[N][M],sum[N][M];
int dp[N][N][N];
int get(int l,int r,int op) {
return sum[r][op] - sum[l][op];
}
void mx(int &a, int b) {
if (a < b) a = b;
}
int main() {
n = fr(), m = fr(), k = fr();
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
w[i][j] = fr();
sum[i][j] = sum[i - 1][j] + w[i][j];
}
}
if (m == 1) {
memset(dp,-0x3f,sizeof dp);
dp[0][0][0] = 0;
for (int i = 1; i <= n; i ++) {
int j = i - 1;
dp[i][0][0] = 0;
for (int t = 1; t <= k; t ++) {
dp[i][t][1] = max({dp[j][t - 1][1],dp[j][t - 1][0],dp[j][t][1]}) + w[i][1];
dp[i][t][0] = max(dp[j][t][0],dp[j][t][1]);
}
}
fw(max(dp[n][k][1],dp[n][k][0]));
ch;
return 0;
}
for (int t = 1; t <= k; t ++) {
for (int i = 1; i <= n; i ++) {
// 第一列
for (int j = 1; j <= n; j ++) {
// 第二列
dp[i][j][t] = max(dp[i - 1][j][t],dp[i][j - 1][t]);
for (int p = 0; p < max(i,j); p ++) {
if (p < i) {
mx(dp[i][j][t],dp[p][j][t - 1] + get(p,i,1));
}
if (p < j) {
mx(dp[i][j][t],dp[i][p][t - 1] + get(p,j,2));
}
if (i == j) {
mx(dp[i][j][t],dp[p][p][t - 1] + get(p,i,1) + get(p,j,2));
}
}
}
}
}
fw(dp[n][n][k]);
return 0;
}
Random Walk to Millionaire
期望
用一下完全平方式来记录和计算然后直接
点击查看代码
lwl ans;
int n,m,k;
vector<int> e[N];
bool flag[N];
bool vis[N][N];
lwl d[N];
lwl dp[3][N][N];
// 0 => x ^ 2 * p 1 => x * p 2 => p
// 哪个点 时间点
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);
}
void bfs() {
queue<pii> q;
q.push({1,0});
dp[2][1][0] = 1;
while (q.size()) {
int u = q.front().fi;
int t = q.front().se;
q.pop();
for (auto v : e[u]) {
dp[0][v][t + 1] += dp[0][u][t] * d[u] % mod;
dp[0][v][t + 1] %= mod;
dp[1][v][t + 1] += dp[1][u][t] * d[u] % mod;
dp[1][v][t + 1] %= mod;
dp[2][v][t + 1] += dp[2][u][t] * d[u] % mod;
dp[2][v][t + 1] %= mod;
if (flag[v]) {
// + x ^ 2
ans += (dp[0][u][t] * d[u]) % mod;
ans %= mod;
} else {
// x ++
dp[0][v][t + 1] += 2 * dp[1][u][t] * d[u] % mod;
dp[0][v][t + 1] += dp[2][u][t] * d[u] % mod;
dp[0][v][t + 1] %= mod;
dp[1][v][t + 1] += dp[2][u][t] * d[u] % mod;
dp[1][v][t + 1] %= mod;
}
if (t + 1 < k && !vis[v][t + 1]) {
vis[v][t + 1] = true;
q.push({v,t + 1});
}
}
}
}
int main() {
n = fr(), m = fr(), k = fr();
for (int i = 1; i <= m; i ++) {
int a = fr(), b = fr();
e[a].push_back(b);
e[b].push_back(a);
d[a] ++, d[b] ++;
}
for (int i = 1; i <= n; i ++) flag[i] = fr();
for (int i = 1; i <= n; i ++) {
d[i] = ny(d[i]);
}
bfs();
fw(ans);
return 0;
}
Day 47
今天的题目都是恶心码量模拟题(除了一道小清新换根题)
P9436 一些数
点击查看代码
int main() {
int T = fr();
while (T --) {
lwl ans = 0;
n = fr(), m = fr();
int cc = 0;
int tt = 0;
for (int i = 1; i <= n; i ++)
qwq[i] = 0,w[i] = 0,sum[i] = 1;
for (int i = 1; i <= m; i ++) {
int a = fr(), b = fr();
q[i] = {a, b};
w[a] = b;
sum[b] = 0;
if (abs(a - b) >= 2) cc ++, tt = a;
}
for (int i = 1; i <= n; i ++) sum[i] += sum[i - 1];
if (cc) {
if (cc > 1) wj;
else {
int t = (w[tt] > t) * 2 - 1;
bool flag = true;
for (int i = 1; i <= n; i ++) {
if (abs(w[i] - i) >= 2) continue;
if (w[i]) continue;
if (tt < i) {
if (w[i] != i + tt && w[i]) {
flag = false;
break;
}
} else {
if (w[i] != i && w[i]) {
flag = false;
break;
}
}
}
if (flag) puts("1");
else puts("1");
}
continue;
}
if (!m) {
fw((lwl)n * (n - 1) - n + 1);
ch;
continue;
}
cc = 0;
for (int i = 1; i <= n; i ++) {
if (!w[i]) continue;
cc += (w[i] != i);
}
q[0].fi = 0,q[m + 1].fi = n + 1;
if (!cc) {
for (int i = 1; i <= m + 1; i ++) {
int len = q[i].fi - q[i - 1].fi - 1;
if (!len) continue;
ans += (lwl)len * (len - 1) - len + 1;
}
fw(ans);
ch;
continue;
}
sort(w + 1, w + 1 + m);
int l = m, r = 1;
for (int i = 1; i <= m; i ++) {
if (q[i].fi != q[i].se) {
l = i;
break;
}
}
for (int i = m; i; i --) {
if (q[i].fi != q[i].se) {
r = i;
break;
}
}
if (q[l].fi < q[l].se) {
bool flag = true;
for (int i = l; i <= r; i ++) {
if (q[i].fi != q[i].se - 1) {
flag = false;
break;
}
}
if (flag) {
ans = (lwl)(q[l].fi - q[l - 1].fi) * (q[r + 1].fi - q[r].fi - 1);
fw(ans);
ch;
continue;
} else {
flag = true;
for (int i = l + 1; i <= n; i ++) {
if (q[i].fi != q[i].se + 1) {
flag = false;
break;
}
}
if (flag) {
ans = (q[l].se >= q[r].fi && q[l].se < q[r + 1].fi);
fw(ans);
ch;
continue;
}
}
}
if (q[r].fi > q[r].se) {
bool flag = true;
for (int i = l; i <= r; i ++) {
if (q[i].fi != q[i].se + 1) {
flag = false;
break;
}
}
if (flag) {
ans = (lwl)(q[l].fi - q[l - 1].fi - 1) * (q[r + 1].fi - q[r].fi);
fw(ans);
ch;
continue;
} else {
flag = true;
for (int i = l; i <= r - 1; i ++) {
if (q[i].fi != q[i].se - 1) {
flag = false;
break;
}
}
if (flag) {
ans = (q[r].se <= q[l].fi && q[r].se > q[l - 1].fi);
fw(ans);
ch;
continue;
}
}
}
wj;
}
return 0;
}
Isolate Elements
因为是一行行变的,所以考虑将每一行作为一个整体,然后像玉米田一样处理(大概吧)
然后直接暴力判断状态就可以了。
点击查看代码
int main() {
n = fr(), m = fr();
for (int i = 1; i <= n; i ++) {
w[i][0] = w[i][m + 1] = inf;
for (int j = 1; j <= m; j ++) {
w[i][j] = fr();
}
}
for (int j = 1; j <= m; j ++) w[0][j] = inf,w[n + 1][j] = inf;
memset(dp,0x3f,sizeof dp);
dp[1][1][1] = dp[1][0][1] = 1;
dp[1][0][0] = dp[1][1][0] = 0;
for (int i = 2; i <= n + 1; i ++) {
int type = -1;
int u = i - 1;
// 上上行不翻转 上行不翻转
for (int j = 1; j <= m; j ++) {
if (w[u][j] != w[u][j - 1] && w[u][j] != w[u][j + 1] &&
w[u][j] != w[u - 1][j]) {
int tmp = -1;
if (w[u][j] == w[i][j]) tmp = 0;
else tmp = 1;
if (type == -1) type = tmp;
else if (type != tmp) type = inf;
}
}
if (type != inf && ~ type)
dp[i][0][type] = min(dp[i][0][type],dp[u][0][0] + type);
else if (type == -1) {
dp[i][0][1] = min(dp[i][0][1],dp[u][0][0] + 1);
dp[i][0][0] = min(dp[i][0][0],dp[u][0][0]);
}
type = -1;
// 上上行翻转 上行不翻转
for (int j = 1; j <= m; j ++) {
if (w[u][j] != w[u][j - 1] && w[u][j] != w[u][j + 1] &&
1 - w[u][j] != w[u - 1][j]) {
int tmp = -1;
if (w[u][j] == w[i][j]) tmp = 0;
else tmp = 1;
if (type == -1) type = tmp;
else if (type != tmp) type = inf;
}
}
if (type != inf && ~ type)
dp[i][0][type] = min(dp[i][0][type],dp[u][1][0] + type);
else if (type == -1) {
dp[i][0][1] = min(dp[i][0][1],dp[u][1][0] + 1);
dp[i][0][0] = min(dp[i][0][0],dp[u][1][0]);
}
type = -1;
// 上上行翻转 上行翻转
for (int j = 1; j <= m; j ++) {
if (w[u][j] != w[u][j - 1] && w[u][j] != w[u][j + 1] &&
w[u][j] != w[u - 1][j]) {
int tmp = -1;
if (1 - w[u][j] == w[i][j]) tmp = 0;
else tmp = 1;
if (type == -1) type = tmp;
else if (type != tmp) type = inf;
}
}
if (type != inf && ~ type)
dp[i][1][type] = min(dp[i][1][type],dp[u][1][1] + type);
else if (type == -1) {
dp[i][1][1] = min(dp[i][1][1],dp[u][1][1] + 1);
dp[i][1][0] = min(dp[i][1][0],dp[u][1][1]);
}
type = -1;
// 上上行不翻转 上行翻转
for (int j = 1; j <= m; j ++) {
if (w[u][j] != w[u][j - 1] && w[u][j] != w[u][j + 1] &&
1 - w[u][j] != w[u - 1][j]) {
int tmp = -1;
if (1 - w[u][j] == w[i][j]) tmp = 0;
else tmp = 1;
if (type == -1) type = tmp;
else if (type != tmp) type = inf;
}
}
if (type != inf && ~ type)
dp[i][1][type] = min(dp[i][1][type],dp[u][0][1] + type);
else if (type == -1) {
dp[i][1][1] = min(dp[i][1][1],dp[u][0][1] + 1);
dp[i][1][0] = min(dp[i][1][0],dp[u][0][1]);
}
}
int ans = min(dp[n + 1][1][0],dp[n + 1][0][0]);
if (ans > n) wj;
else fw(ans);
return 0;
}
P8188 Email Filing S
直接用优先队列模拟。
点击查看代码
int main() {
int T = fr();
while (T --) {
n = fr(), m = fr(), k = fr();
memset(cnt,0,sizeof cnt);
memset(flag,0,sizeof flag);
for (int i = 1; i <= m; i ++) {
h[i] = fr();
cnt[h[i]] ++;
flag[i] = false;
}
bool vis = true;
priority_queue<pii,vector<pii>,greater<pii> > q;
// 窗口里面的邮件
deque<int> t; // 跳过的邮件
for (int i = 1; i <= k; i ++)
q.push({h[i],i});
int l = 1, r = k, u = 1;
while (q.size() || t.size()) {
while (!cnt[u] && u < n - k + 1)
u ++;
if (q.size() && q.top().fi >= u && q.top().fi <= u + k - 1) {
cnt[q.top().fi] --;
flag[q.top().se] = true;
q.pop();
if (r < m) {
r ++;
q.push({h[r],r});
}
} else if (r < m) {
// 窗口往下滑
r ++;
q.push({h[r],r});
t.push_back(h[l]);
l ++;
} else if ((int)q.size() < k && t.size()) {
l --;
q.push({t.back(),l});
t.pop_back();
} else {
vis = false;
break;
}
while (q.size() && q.top().se < l)
q.pop();
while (flag[l]) l ++;
}
if (vis && q.empty()) yj;
else wj;
}
return 0;
}
P3047 Nearby Cows G
小清新换根
点击查看代码
void dfs(int u,int fa) {
dp[u][0] = w[u];
for (auto v : e[u]) {
if (v == fa) continue;
dfs(v,u);
for (int t = k; t; t --)
dp[u][t] += dp[v][t - 1];
}
}
void get(int u,int fa) {
f[u][0] = w[u];
for (auto v : e[u]) {
if (v == fa) continue;
for (int t = k; t; t --) {
f[v][t] += f[u][t - 1];
}
for (int t = k; t >= 2; t --) {
f[v][t] += dp[u][t - 1] - dp[v][t - 2];
}
get(v,u);
}
}
int main() {
n = fr(), k = fr();
for (int i = 1; i < n; i ++) {
int a = fr(), b = fr();
e[a].push_back(b);
e[b].push_back(a);
}
for (int i = 1; i <= n; i ++)
w[i] = fr();
dfs(1,0);
get(1,0);
for (int u = 1; u <= n; u ++) {
int ans = w[u];
for (int i = 1; i <= k; i ++) {
ans += dp[u][i] + f[u][i];
}
fw(ans);
ch;
}
return 0;
}
Day 48
P9234 买瓜
折半搜索,复杂度大概是
就说一说主要的剪枝:
- 排序顺序优化,先从小到大排序再
- 最优性剪枝,如果说有比这个更优的答案,就不继续遍历了
- 可行性剪枝,如果说当前的总重量大于所需的总重量就直接返回
然后因为这一题要砍一半,所以一开始可以将所有重量都乘二来做。
一开始因为用
点击查看代码
void dfs1(int u,lwl sum,int cnt) {
if (sum > m) return ;
if (cnt > ans) return ;
if (h.count(sum) && cnt > h[sum]) return;
if (sum == m) ans = min(ans,cnt);
if (u > n / 2) {
if (!h.count(sum)) h[sum] = cnt;
else h[sum] = min(h[sum],cnt);
return ;
}
dfs1(u + 1,sum + w[u],cnt);
dfs1(u + 1,sum + (w[u] >> 1),cnt + 1);
dfs1(u + 1,sum,cnt);
}
void dfs2(int u,lwl sum,int cnt) {
if (h.count(sum)) ans = min(ans,h[sum] + cnt);
if (cnt > ans) return ;
if (u > n) return ;
dfs2(u + 1,sum - w[u],cnt);
dfs2(u + 1,sum - (w[u] >> 1),cnt + 1);
dfs2(u + 1,sum,cnt);
}
int main(){
n = fr(), m = fr() << 1;
for (int i = 1; i <= n; i ++) w[i] = fr() << 1;
sort(w + 1,w + 1 + n);
for (int i = 1; i <= n; i ++) {
s += w[i];
}
if (s < m) {
wj;
return 0;
}
dfs1(1,0,0);
dfs2(n / 2 + 1,m,0);
if (ans >= inf / 2) wj;
else fw(ans);
return 0;
}
P7382 Simfonija
应该算是一道思维题?复杂度瓶颈在排序那里。
可以想到,先修改某个数和先加之后再修改某个数的贡献是一样的,因为最优方案肯定是最后将他变成和
所以说我们就是要选择一段
选区间的话直接暴力枚举就可以了,对于找到一个数
这个式子的形式就有点像距离公式,所以问题就转变成了数轴上有
毫无疑问,如果数轴上的点的数量是奇数的话(设
所以说先将
点击查看代码
int n,K;
pii w[N];
int c[N];
lwl sum[N];
lwl get(int l,int r) {
return sum[r] - sum[l - 1];
}
int main(){
n = fr(), K = fr();
for (int i = 1; i <= n; i ++) w[i].fi = fr();
for (int i = 1; i <= n; i ++) w[i].se = fr();
if (n == K) {
puts("0");
return 0;
}
for (int i = 1; i <= n; i ++) {
c[i] = w[i].se - w[i].fi;
}
sort(c + 1, c + 1 + n);
for (int i = 1; i <= n; i ++) {
sum[i] = sum[i - 1] + c[i];
}
int k = n - K;
lwl ans = linf;
for (int i = 1; i <= n - k + 1; i ++) {
int j = i + k - 1;
int t1 = (i + j) >> 1,t2;
if ((i + j) & 1) t2 = t1 + 1;
else t2 = t1;
lwl x = (c[t1] + c[t2]) >> 1;
ans = min(ans,(t1 - i + 1) * x - get(i,t1) + get(t2,j) - (j - t2 + 1) * x);
}
fw(ans);
return 0;
}
Grid 2
一点前置:如果从
假设
然后要把前面经过的障碍点都去掉,其实这个就直接
点击查看代码
int n;
int H,W;
pii w[N];
lwl f[M],nf[M];
lwl dp[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);
}
void init() {
f[0] = 1;
for (int i = 1; i <= m; i ++) {
f[i] = i * f[i - 1] % mod;
}
nf[m] = ny(f[m]);
for (int i = m - 1; i >= 0; i --) {
nf[i] = nf[i + 1] * (i + 1) % mod;
}
}
lwl C(int a,int b) {
if (!b) return 1;
return f[a] * nf[a - b] % mod * nf[b] % mod;
}
int main(){
H = fr(), W = fr(), n = fr();
for (int i = 1; i <= n; i ++) {
int a = fr(), b = fr();
w[i] = {a, b};
}
init();
lwl ans = C(H - 1 + W - 1,H - 1);
sort(w + 1,w + 1 + n);
for (int i = 1; i <= n; i ++) {
dp[i] = C(w[i].fi - 1 + w[i].se - 1,w[i].fi - 1);
for (int j = 1; j < i; j ++) {
if (w[i].se < w[j].se) continue;
dp[i] = (dp[i] - dp[j] * C(w[i].fi - w[j].fi + w[i].se - w[j].se,w[i].fi - w[j].fi) % mod) % mod;
}
}
for (int i = 1; i <= n; i ++) {
ans = (ans - dp[i] * C(H - w[i].fi + W - w[i].se,H - w[i].fi) % mod) % mod;
}
fw((ans + mod) % mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】