noip模拟【20190813】
T1
[树状数组,线段树]
暴力O(nm)->60pts
Noip2018:ans = sigma{max(0,a[i+1]-a[i])};
令b[i+1] = a[i+1]-a[i];
区间增加(l,r)只会影响端点(l or r)的b[i]值。
考虑用树状数组维护答案。
开两个树状数组,一个记录实际值,一个记录与0相比的较大值
时间复杂度O(mlogn)
【code】
#include<bits/stdc++.h> using namespace std; #define ll long long #define File "skyscraper" inline void file(){ freopen(File".in","r",stdin); freopen(File".out","w",stdout); } inline int read(){ int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch=='-')f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x<<1) + (x<<3) + ch-'0'; ch = getchar();} return x*f; } const int mxn = 1e5 + 10; int n,m; int a[mxn],b[mxn]; ll c[mxn][2]; inline int lowbit(int x){ return x&(-x); } inline void update(int x,int d,int k){ while(x <= n){ c[x][k] += d; x += lowbit(x); } } inline ll query(int x,int k){ ll ret(0); while(x){ ret += c[x][k]; x -= lowbit(x); } return ret; } int main(){ file(); n = read(),m = read(); for(int i = 1;i <= n; ++i){ a[i] = read(); b[i] = a[i]-a[i-1]; update(i,b[i],0); if(b[i] > 0) update(i,b[i],1); } while(m--){ int opt = read(),l = read(),r = read(); if(opt==1){ int k = read(); update(l,k,0),update(r+1,-k,0); if(b[l] > 0) update(l,-b[l],1); if(b[r+1] > 0) update(r+1,-b[r+1],1); b[l] += k,b[r+1] -= k; if(b[l] > 0) update(l,b[l],1); if(b[r+1] > 0) update(r+1,b[r+1],1); }else{ printf("%lld\n",query(l,0)+query(r,1)-query(l,1)); } } return 0; } /* 5 4 1 3 1 4 5 2 1 5 1 3 4 2 2 2 4 2 1 5 */ /* 7 6 6 */
T2
[dfs序]
找到图中所有叶子节点,求出所有叶子节点的dfs序,这样保证了离得够远,把第i个点连向i+m/2个点时,假如不在一个子树内,形成一个经过root的环。如果它们同时属于一个子树,那么说明这个子树很大,一定可以有一个属于这个子树的叶子连到别的子树中去。
【code】
#include<bits/stdc++.h> using namespace std; #define ll long long #define File "network" inline void file(){ freopen(File".in","r",stdin); freopen(File".out","w",stdout); } inline int read(){ int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch=='-')f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x<<1) + (x<<3) + ch-'0'; ch = getchar();} return x*f; } const int mxn = 1e5+5; int n,m,h; struct edge{ int y,nxt; }e[mxn<<1]; int to[mxn],len; inline void add(int xx,int yy){ e[++len].nxt = to[xx]; to[xx] = len; e[len].y = yy; } struct P{ int id,dfn; }p[mxn]; inline bool cmp(P t1,P t2){ return t1.dfn < t2.dfn; } int rt; int b[mxn],deg[mxn]; int tot(0); int dfn[mxn]; void dfs(int x,int fa){ dfn[x] = ++tot; for(int i = to[x]; i;i = e[i].nxt){ int y = e[i].y; if(y == fa) continue; dfs(y,x); } } int main(){ // file(); n = read(),h = read(); for(int i = 1;i < n; ++i){ int x = read(),y = read(); deg[x]++,deg[y]++; add(x,y),add(y,x); } tot = 0; dfs(h,-1); tot = 0; for(int i = 0;i < n; ++i){ if(!deg[i]) p[++tot].id = i,p[tot].dfn = dfn[i]; } sort(p+1,p+tot+1,cmp); printf("%d\n",(tot+1)/2); for(int i = 1;i <= tot/2; ++i) printf("%d %d\n",p[i].id,p[i+tot/2].id); if(!(tot&1)) printf("%d %d\n",p[1].id,p[tot].id); return 0; } /* 4 0 0 1 0 2 0 3 */
T3
[dp]
交换k次路灯的操作就是将没有选的集合中挑出代价最小的不超过k个代价,替换掉已选集合中较大的几个代价。
贪心:
或者说,选的集合要么是2、5、8… (%3==1)+1
要么是1、4、7…(%3==0)+1
好,上面那个做法假了。
交换的一定是一盏亮的灯和一盏灭的灯。
设f[i][x][y][a][b]表示考察前i盏灯,最后两盏灯的亮灭状态为x、y,已经有a盏亮的灯被换走,已经有b盏灭的灯被换上去的最小代价。
几种转移,点灯,不点灯,点亮后被换走,不点从别的地方
如果x,y都是亮着的,f[i][y][0][a][b] = min(f[i][y][0][a][b],f[i-1][x][y][a][b]);
如果灭的灯还能被换上去,f[i][y][1][a][b+1] = min(f[i][y][1][a][b+1],f[i-1][x][y][a][b]);
如果亮着的灯还没被换走,而且前两盏灯都是亮的,
f[i][y][0][a+1][b] = min(f[i][y][0][a+1][b],f[i-1][x][y][a][b]+val);
【code】
#include <bits/stdc++.h> using namespace std; const int K = 10; const int N = 2.5e5 + 5; const long long INF = 1e17; int n, k; int w[N]; long long dp[2][2][2][K][K]; bool Chkmin(long long &a, long long b) { return (a > b)? (a = b, 1) : (0); // if(a>b) a = b, return 1; // else return 0 } inline int read(){ int num = 0; bool f = 1;char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9') {num = num * 10 + ch - '0';ch = getchar();} return num = f ? num: -num; } int main() { freopen("light.in","r",stdin); freopen("light.out","w",stdout); scanf("%d%d", &n, &k); for (int i = 1; i <= n; ++i) { w[i] = read(); } memset(dp, 0x3f, sizeof dp); dp[0][1][0][0][0] = 0; for (int i = 0; i < n; ++i) { int nxt = ~i & 1, pre = i & 1, val = w[i + 1]; memset(dp[nxt], 0x3f, sizeof dp[nxt]); for (int a = 0; a <= k; ++a) { for (int b = 0; b <= k; ++b) { for (int x = 0; x < 2; ++x) { for (int y = 0; y < 2; ++y) { if (dp[pre][x][y][a][b] > INF) continue; Chkmin(dp[nxt][y][1][a][b], dp[pre][x][y][a][b] + val); if (x || y) Chkmin(dp[nxt][y][0][a][b], dp[pre][x][y][a][b]); if (b < k) Chkmin(dp[nxt][y][1][a][b + 1], dp[pre][x][y][a][b]); if (a < k && (x || y)) Chkmin(dp[nxt][y][0][a + 1][b], dp[pre][x][y][a][b] + val); } } } } } long long ans = INF; for (int i = 0; i <= k; ++i) { for (int x = 0; x < 2; ++x) { for (int y = 0; y < 2; ++y) { if (x || y) { ans = min(ans, dp[n & 1][x][y][i][i]); } } } } printf("%lld\n", ans); return 0; }