• 29-30 考试总结


    来源:(COCI2011)~(2012) (test1)-(test2)部分题目。

    T1 ples

    题目大意

    (N)个男生和(N)个女生参加舞会,跳舞时只能是一个男生一个女生,我们知道每个人的身高,同时某个男生只想和比他高(或者矮)的女生跳(有人跟你跳还挑三拣四的?),女生也是一样的,身高一样的人都不想与对方跳。给出所有数据,输出满足人们希望的前提下组成的最多对数。

    分析

    开始拿到题看了看,哦?二分图最大匹配板子?

    直到看到了浩瀚无边的数据范围。。。正当心中默念"哦豁"时,看到了很小的值域范围。。。

    人生大起大落真刺激

    恩,带权二分图最大匹配,因为值域很小,我们可以把每个值看做点,人数看做点权,按题目要求连边,男生女生分别连向源点和汇点,然后就是跑最大流了。

    因为内存限制,所以要精确算开多少条边。

    代码

    #include<queue>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    const int BASE = 1500; 
    const int INF = 0x3f3f3f3f;
    inline int read(){
    	int f = 1, x = 0; char ch;
    	do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
    	do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    	return f * x;
    }
    inline void hand_in() {
    	freopen("ples.in", "r", stdin);
    	freopen("ples.out", "w", stdout);
    } 
    int n, s, t, maxflow;
    struct Node{ int l, r; }A[1005], B[1005];
    inline int min(int a, int b) { return a < b ? a : b; } 
    struct Sakura { int to, nxt, w; }sak[2200005]; int head[4010], cnt = 1;
    inline void add(int x, int y, int w) {
    	++cnt;
    	sak[cnt].to = y, sak[cnt].w = w, sak[cnt].nxt = head[x], head[x] = cnt;
    	++cnt;
    	sak[cnt].to = x, sak[cnt].w = 0, sak[cnt].nxt = head[y], head[y] = cnt;
    //	printf("%d -> %d : %d
    ", x, y, w);
    }
    
    int dep[4010];
    inline bool bfs() {
    	std :: queue<int> q;
    	memset(dep, 0, sizeof dep);
    	dep[s] = 1, q.push(s);
    	while(!q.empty()) {
    		int u = q.front();
    		q.pop();
    		for (int i = head[u];i;i =sak[i].nxt) {
    			int v = sak[i].to, w = sak[i].w;
    			if (!dep[v] && w) {
    				dep[v] = dep[u] + 1;	
    				if (v == t) return 1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    
    inline int dfs(int u, int flow) {
    	if (u == t) return flow;
    	int rest = flow, rlow;
    	for (int i = head[u];i && rest;i = sak[i].nxt) {
    		int v = sak[i].to, w = sak[i].w; 
    		if (w && dep[v] == dep[u] + 1) {
    			rlow = dfs(v, min(rest, w));
    			if (!rlow) dep[v] = 0;
    			sak[i].w -= rlow;
    			sak[i ^ 1].w += rlow;
    			rest -= rlow;
    		}
    	}
    	return flow - rest;
    }
    
    inline int Dinic() {
    	int lowflow;
    	while (bfs()) {
    		while(lowflow = dfs(s, INF)) maxflow += lowflow;
    	}
    	return maxflow;
    }
    
    int main(){
    	hand_in();
    	n = read();
    	s = 4004, t = 4005;
    	for (int i = 1, x;i <= n; ++i) {
    		x = read();
    		if (x < 0) A[- x - BASE].l ++;
    		else A[x - BASE].r ++;
    	}
    
    	for (int i = 1, x;i <= n; ++i) {
    		x = read();
    		if (x < 0) B[- x - BASE].l ++;
    		else B[x - BASE].r ++;
    	}
    
    	/* A.l 0~1000 A.r 1001~2001 B.l 2002~3002  B.r 3003~4003 */
    
    	/* S --- A.l / A.r 连源点 */
    	for (int i = 0;i <= 1000; ++i) {
    		if (A[i].l) add(s, i, A[i].l);
    		if (A[i].r) add(s, i + 1001, A[i].r);
    	}
    
    	/* A.l --- B.r 男高女矮 */
    	for (int i = 1;i <= 1000; ++i) {
    		for (int j = 0;j < i; ++j) {
    			if (A[i].l && B[j].r) add(i, j + 3003, INF);
    		}
    	}
    
    	/* A.r --- B.l 男矮女高 */
    	for (int i = 0;i < 1000; ++i) {
    		for (int j = i + 1;j <= 1000; ++j) {
    			if (A[i].r && B[j].l) add(i + 1001, j + 2002, INF);
    		}
    	}
    
    	/* B.l / B.r --- T 连汇点 */
    	for (int j = 0;j <= 1000; ++j) {
    		if (B[j].l) add(j + 2002, t, B[j].l);
    		if (B[j].r) add(j + 3003, t, B[j].r);
    	}
    
    	printf("%d", Dinic());
    	return 0;
    }
    

    排序+贪心是正解???

    好吧,的确是的。

    我们可以发现,排序后让正数与负数匹配,用指针一直向后移动,那么会让后面的情况达到最优。

    T2 sort

    题目大意

    对一个序列使用(reverse)函数排成升序,每次(reverse)序列中所有连续单调下降子序列,保证在第一次排序后,所有的连续单调下降子序列的长度都是偶数。求(reverse)函数的使用次数。

    分析

    30pt做法

    直接用(reverse)函数模拟,你甚至可以自己手写

    100pt做法

    首先,有个结论,在第一次对序列执行这种方法的排序后,序列中所有的连续单调下降子序列的长度要么是(3)要么是(2)(1)排除在外)。

    证明:考虑相邻的两个连续单调下降子序列,有两种情况:它们中间没有间隔;它们中间有一个元素的间隔。对于第一种情况,(reverse)这两个子序列后,只会在它们俩的交界处形成一个长度为(2)的单调下降序列;对于第二种情况,同理,会形成一个长度为(3)的单调下降序列。

    又因为题目保证都为偶数,所以在第一次排序后,所有的连续单调下降子序列的长度都是(2)

    这有什么性质?在之后我们每次(reverse),就会让一个数的前面比它大的数减(1),又因为递增序列所有数都比它前面的数大,所以联想到求逆序对。

    所以这道题的解法就是先模拟排序一遍该序列,累加答案,然后计算当前序列的逆序对数,累加进答案里面。

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    const int N = 100000 + 5;
    using std :: reverse;
    inline int read(){
    	int f = 1, x = 0; char ch;
    	do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
    	do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    	return f * x;
    }
    inline void hand_in() {
    	freopen("sort.in", "r", stdin);
    	freopen("sort.out", "w", stdout);
    } 
    int n, a[N];
    ll ans;
    inline bool judge() {
    	for (int i = 2;i <= n; ++i) {
    		if (a[i] < a[i - 1]) return 1;
    	}
    	return 0;
    }
    int c[N];
    inline int lowbit(int x) {
    	return x & (-x);
    }
    inline void add(int x) {
    	for (int i = x;i <= n; i += lowbit(i)) c[i] ++;
    }
    inline int ask(int x) {
    	int res = 0;
    	if (!x) return 0;
    	for (int i = x;i;i -= lowbit(i)) res += c[i];
    	return res;
    }
    inline void solve() {
    	int st = 1, ed = 1, len = 1;
    	for (int i = 2;i <= n; ++i) {
    		if (a[i] < a[i - 1]) {
    			ed = i, len ++;
    		}
    		else {
    			if (len > 1) {
    				reverse(a + st, a + ed + 1);		
    				ans ++;		
    			}
    			st = i, ed = i, len = 1;
    		}
    	}
    	if (len > 1) {
    		reverse(a + st, a + ed + 1);		
    		ans ++;
    	}
    	for (int i = n;i >= 1; --i) {
    		ans += ask(a[i] - 1);
    		add(a[i]);
    	}
    }
    int main(){
    	hand_in();
    	n = read();
    	for (int i = 1;i <= n; ++i) a[i] = read(); 
    	solve();
    	printf("%lld", ans);
    	return 0;
    }
    

    T3 skakac(To be continue)

    题目大意

    略略略~

    分析

    20pt做法

    也许直接(dfs)

    30~50pt做法

    (f[i][j][k])表示当前时间为(i),棋盘位置为((j,k))是否到达的状态,然后就可以枚举(i-1)的状态转移过来,用个滚动数组优化下,空间复杂度(O(n^2)),时间复杂度(O(Tn^2))

    同时,也可以上(BFS)

    60~100pt做法

    鬼知道数据有多恐怖,貌似正确的复杂度只有(70pt)???

    考虑压缩每一行的状态,然后就可以(O(Tn))转移。

    难就难在处理图上的(K_{i,j})

    为了保证时间复杂度和空间复杂度,对(K_{i,j})分开处理。

    如果(K_{i,j} > 1000)那么把在(T)以内的(K_{i,j})的倍数记录下来,当(T)达到这个值的时候再处理。 如果(K_{i,j}le 1000),可以把(K_{i,j})分解质因数,设=(K_{i,j}=p_1^{c_1}p_2^{c_2}...p_k^{c_k}),那么当(T)能同时整除(_1^{c_1}p_2^{c_2}...p_k^{c_k}) 时当前((i,j))就可到达。
    (g[i][j][k])表示第k行能整除(p_i^j)时的情况,那么把(T)也分解质因数,棋盘的情况就可以表示为(g[1][c_1] & g[2][c_2] ... g[k][c_k])
    当然次数为(0)的也要考虑进去,为了节省时间可以令(h[i][j]=g[i][0]&g[i+1][0]...g[j][0])

    然后就可以预处理出所有状态下图对应的状态,也许写得有点问题,数据点过不全。

    代码

    #include<vector>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define Re register
    const int P = 1000000007;
    const int BASE = 1e6;
    const int N = 30 + 5;
    using std :: vector;
    using std :: sort;
    inline void hand_in() {
    	freopen("skakac.in", "r", stdin);
    	freopen("skakac.out", "w", stdout);
    }
    inline int read(){
    	int f = 1, x = 0; char ch;
    	do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
    	do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    	return f * x;
    }
    int n, t, st_x, st_y;
    int mp[N][N];
    
    struct Node {
    	int t, x, y;
    	Node (int a = 0, int b = 0, int c = 0) : t(a), x(b), y(c) {}
    	friend bool operator < (Node a, Node b) { return a.t < b.t; }
    }ans[N * N], rate[BASE + 5]; int oo, pp;
    
    /* 预处理质数 */
    int prim[BASE + 5], vis[BASE + 5], tot, ps;
    inline void init() {
    	for (Re int i = 2;i <= BASE; ++i) {
    		if (!vis[i]) {
    			prim[++tot] = i, vis[i] = tot;
    			if (i <= 1000) ps = tot; /* 小优化:记录下1000以内的质数到了哪里 */
    		}
    		for (Re int j = 1;j <= tot; ++j) {
    			if ((ll)i * (ll)prim[j] > BASE) break;
    			vis[i * prim[j]] = j;
    			if (i % prim[j] == 0) break;
    		}
    	}
    }
    
    /* 对(x,y)上的k值进行质因数分解 */
    /*
    h: h[i][j][k]表示 
    g: g[i][j][k]表示第k行能整除pi^j时的情况 
    */
    int h[250][250][35], g[250][25][35];
    inline void divide(int k, int x, int y) {
    	for (Re int i = 1;i <= ps; ++i) {
    		int ret = 0;
    		while (k % prim[i] == 0) ret ++, k /= prim[i];
    		/* 既然x^ret能够到达,所以高于ret的次幂都行,最多达到2^20 */
    		for (;ret <= 20; ++ret) g[i][ret][x] |= (1 << y);
    	}
    }
    
    /* 质因数分解 */
    struct Divide {
    	int pr, nm;
    	friend bool operator < (Divide a, Divide b) { return a.pr < b.pr; }
    }dv[BASE + 5]; int w[35];
    inline void work(int x) {
    	int ct = 0, pos = 1;
    
    	/* 会超时??? */
    //	for (int i = 1, ret;prim[i] <= x; ++i) {
    //		if (x % prim[i]) continue;
    //		ret = 0;
    //		while (x % prim[i] == 0) ret ++, x /= prim[i];
    //		if (ret) dv[++ct].pr = i, dv[ct].nm = ret;
    //	}
    
    	while (x ^ 1) {
    		int ret = 0, op = vis[x];
    		while (x % prim[op] == 0) ret ++, x /= prim[op];
    		dv[++ct].pr = op, dv[ct].nm = ret;		
    	}
    	sort(dv + 1, dv + 1 + ct);
    	
    	for (int i = 1;i <= ct && dv[i].pr <= ps; ++i) {
    		for (int j = 1;j <= n; ++j) {
    			w[j] &= g[dv[i].pr][dv[i].nm][j];
    		}
    		if (pos < dv[i].pr) {
    			for (int j = 1;j <= n; ++j) {
    				w[j] &= h[pos][dv[i].pr - 1][j];
    			}
    		}
    		pos = dv[i].pr + 1;
    	}
    	if (pos < ps) for (int i = 1;i <= n; ++i) w[i] &= h[pos][ps][i]; 
    }
    
    int f[2][35], now, pre;
    int main(){
    	hand_in();
    
    	/* 读入 */
    	n = read(), t = read(), st_x = read(), st_y = read();
    
    	/* 预处理出质数 */
    	init();
    
    	/* 对地图分类处理:1000以上用倍数,反之分解它 */
    	for (Re int i = 1;i <= n; ++i) {
    		for (Re int j = 1;j <= n; ++j) {
    			mp[i][j] = read();
    			if (mp[i][j] >= 1500) {
    				for (Re int k = 1;k * mp[i][j] <= t; ++k) rate[++pp] = Node(k * mp[i][j], i, j);
    			}
    			else {
    				divide(mp[i][j], i, j);
    			}
    		}
    	}
    
    	/* 排序后可以依次考虑 */
    	sort(rate + 1, rate + 1 + pp);
    	for (Re int i = 1;i <= ps; ++i) {
    		for (Re int j = i;j <= ps; ++j) {
    			for (Re int s = 1;s <= n; ++s) {
    				/* 初始化全为1 */
    				h[i][j][s] = (1 << (n + 1)) - 1;
    				for (Re int k = i;k <= j; ++k) {
    					h[i][j][s] &= g[k][0][s];
    				}
    			}
    		}
    	}
    	f[0][st_x] |= (1 << st_y);
    	for (Re int i = 1, p = 1;i <= t; ++i) {
    		now ^= 1, pre = now ^ 1;
    		for (Re int j = 1;j <= n; ++j) f[now][j] = 0, w[j] = (1 << (n + 1)) - 1;
    		for (Re int j = 1;j <= n; ++j) {
    			if (j > 1) f[now][j] |= (f[pre][j - 1] >> 2) | (f[pre][j - 1] << 2);
    			if (j < n) f[now][j] |= (f[pre][j + 1] >> 2) | (f[pre][j + 1] << 2);
    			if (j > 2) f[now][j] |= (f[pre][j - 2] >> 1) | (f[pre][j - 2] << 1);
    			if (j < n - 1) f[now][j] |= (f[pre][j + 2] >> 1) | (f[pre][j + 2] << 1);
    		}
    		work(i);
    		while (p <= pp && rate[p].t == i) w[rate[p].x] |= (1 << rate[p].y), ++p;
    		for (Re int j = 1;j <= n; ++j) f[now][j] &= w[j];
    	}
    	for (Re int i = 1;i <= n; ++i) {
    		for (Re int j = 1;j <= n; ++j) {
    			if (f[now][i] & (1 << j)) ans[++oo].x = i, ans[oo].y = j;
    		}
    	}
    	printf("%d
    ", oo);
    	for (Re int i = 1;i <= oo; ++i) {
    		printf("%d %d
    ", ans[i].x, ans[i].y);
    	}
    	return 0;
    }
    

    T4 kom

    题目大意

    给出(N)个互不相同的正整数,统计共有多少对数,它们有公共的一个数字(不一定在同一位置上)。

    分析

    20pt做法

    无脑暴力。

    60pt做法

    假的容斥。

    考试时最先想到这个方法。

    对每个数用二进制状态存下来(O(n))扫描,然后对1024种状态容斥一下,就是(O(n imes 1024))

    貌似这种方法用bitset优化下就能过?

    100pt做法

    我们不需要知道所有数,只需要知道某个数出现了哪些数字,思考压缩状态,即只用出现了哪些数字来表示一个数,又因为数字只有(0)~(9),所以整个状态总量就是(2^{10}=1024)。然后我们就可以枚举压缩后的状态啦,记得不能算自身,所以要减去自己与自己成对的情况。

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #define ll long long
    inline void hand_in() {
    	freopen("kom.in", "r", stdin);
    	freopen("kom.out", "w", stdout);
    }
    int n, len, v, pan[1025];
    char ch[20]; ll ret;
    int main(){
    	hand_in();
    	scanf("%d", &n), v = (1 << 10);
    	for (int i = 1, a;i <= n; ++i) {
    		scanf("%s", ch + 1);
    		len = strlen(ch + 1), a = 0;
    		for (int j = 1;j <= len; ++j) {
    			a |= (1 << (ch[j] - '0'));
    		}
    		pan[a] ++;
    	}
    	for (int i = 0;i < v; ++i) {
    		for (int j = 0;j < v; ++j) {
    			if (i & j) {
    				ret += (ll)pan[i] * (ll)pan[j];
    				if (i == j) ret -= (ll)pan[i];
    			}
    		}
    	}
    	printf("%lld
    ", ret >> 1);
    	return 0;
    }
    

    T5 fun

    题目大意

    略略略~

    分析

    30pt做法

    直接按照那个函数用(dfs)模拟即可。

    100pt做法

    我们可以发现,若一层循环中上下界为常数项,我们可以把它换在任何位置,对答案没有任何影响,只需要在统计答案时乘上这层循环的循环次数即可。

    推广一下,当我们确定一个字母变量的值的时候,那么所有依赖于它的变量循环都可变为常数循环,我们任意交换是没有问题的。根据依赖关系,我们会发现是由一棵棵树组成的森林,每棵树相互之间是不会受到影响的,所以直接把每棵树相乘即为最终答案。

    对于每棵树,我们从上往下遍历,就能依次确定各个变量的取值范围,然后将子树的值相乘,再上传就是答案。

    用记忆化优化下复杂度。

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #define ll long long
    const int P = 1000000007;
    const int N = 30;
    inline void hand_in() {
    	freopen("fun.in", "r", stdin);
    	freopen("fun.out", "w", stdout);
    }
    inline bool is_num(char *s) { if (s[1] < '0' || s[1] > '9') return 0; return 1; }
    int cnt, to[N << 1], nxt[N << 1], head[N]; inline void add(int x, int y) { ++cnt; to[cnt] = y, nxt[cnt] = head[x], head[x] = cnt; }
    inline int change_num(char *s) { int l = strlen(s + 1); int res = 0; for (int i = 1;i <= l; ++i) { res = res * 10 + s[i] - '0'; } return res; }
    
    ll dfs(int, int);
    ll f[N][100005], ans = 1;
    int n, l[N], r[N], rate[N];
    char ls[10], rs[10];
    
    inline ll find(int now, int lim) {
    	ll res = 1;
    	int p = head[now], nt;
    	while (p) {
    		nt = to[p];
    		res *= dfs(nt, lim) % P;
    		p = nxt[p];
    	}
    	return res;
    }
    
    inline ll dfs(int now, int lim) {
    	if (~f[now][lim]) return f[now][lim];
    	int last = lim;
    	if (rate[now] == 0) { 
    		while (last <= r[now] && f[now][last] == -1) last ++;
    		if (last > r[now]) f[now][last] = 0;
    		last --;
    		while (last >= lim) {
    			f[now][last] = (f[now][last + 1] + find(now, last)) % P;
    			last --;
    		}
    	}
    	else if (rate[now] == 1) { 
    		while (last >= l[now] && f[now][last] == -1) last --;
    		if (last < l[now]) f[now][last] = 0;
    		last ++;
    		while (last <= lim) {
    			f[now][last] = (f[now][last - 1] + find(now, last)) % P;
    			last ++;
    		}		
    	}
    	else { 
    		f[now][lim] = 0;
    		for (int i = l[now];i <= r[now]; ++i) {
    			f[now][i] = find(now, i);
    			f[now][lim] = (f[now][i] + f[now][lim]) % P;
    		}
    	}
    	return f[now][lim];
    }
    
    int main(){
    //	hand_in();
    	scanf("%d", &n);
    	memset(f, -1, sizeof f);
    	for (int i = 0;i < n; ++i) {
    		scanf("%s %s", ls + 1, rs + 1);
    		rate[i] = -1;  
    		if (is_num(ls)) {
    			l[i] = change_num(ls);
    		}
    		else {
    			rate[i] = 0; 
    			l[i] = ls[1] - 'a';
    			add(l[i], i);
    		}
    
    		if (is_num(rs)) {
    			r[i] = change_num(rs);
    		}
    		else {
    			rate[i] = 1; 
    			r[i] = rs[1] - 'a';
    			add(r[i], i);
    		}
    	}
    	for (int i = 0;i < n; ++i) {
    		if (rate[i] == -1) {
    			ans = ans * dfs(i, 0) % P;
    		}
    	}
    	printf("%lld", ans);
    	return 0;
    }
    
    

    T6 ras

    题目大意

    略略略~

    分析

    30pt做法

    把计算答案的式子列出来后会发现可以贪心。

    即把序列按(t)的升序排序,然后直接计算答案。

    对于每次修改,暴力修改,再重新排遍序,再计算答案。

    100pt做法

    思路大体没错,算法的瓶颈在于修改,也就是动态维护前缀和。

    考虑使用数据结构权值线段树或权值树状数组。

    可以把维护前缀和拆成两个操作,清除某位上的一个数,把一个数插入到某位上。

    在操作的同时对(ans)进行维护。

    注意需要开(ll)

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    using std :: sort;
    const int N = 100000 + 5;
    inline int read(){
    	int f = 1, x = 0; char ch;
    	do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
    	do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    	return f * x;
    }
    inline void hand_in() {
    	freopen("ras.in", "r", stdin);
    	freopen("ras.out", "w", stdout);
    }
    int n, c; ll ans, s;
    struct Data { int l, t; }mk[200005], rsd[200005];
    inline bool cmp(const Data &a, const Data &b) { return a.t < b.t; }
    
    struct Segment_Tree {
    	struct Node {
    		int l, r;
    		ll x, t;
    	}tr[N << 2];
    	
    	#define ls (p << 1)
    	#define rs ((p << 1) | 1)
    	
    	inline void build(int p, int l, int r) {
    		tr[p].l = l, tr[p].r = r;
    		if (l == r) return;
    		int mid = (l + r) >> 1;
    		build(ls, l, mid), build(rs, mid + 1, r);
    	}
    	
    	inline void change(int p, int x, int a, int b) {
    		tr[p].x += a, tr[p].t += b;
    		if (tr[p].l == tr[p].r) return;
    		int mid = (tr[p].l + tr[p].r) >> 1;
    		if (x <= mid) change(ls, x, a, b);
    		else change(rs, x, a, b);
    	}
    	
    	inline ll ask_x(int p, int l, int r) {
    		if (l <= tr[p].l && tr[p].r <= r) {
    			return tr[p].x;
    		}
    		int mid = (tr[p].l + tr[p].r) >> 1;
    		ll res = 0;
    		if (l <= mid) res += ask_x(ls, l, r);
    		if (r > mid) res += ask_x(rs, l, r);
    		return res;
    	}
    	
    	inline ll ask_t(int p, int l, int r) {
    		if (l <= tr[p].l && tr[p].r <= r) {
    			return tr[p].t;
    		}
    		int mid = (tr[p].l + tr[p].r) >> 1;
    		ll res = 0;
    		if (l <= mid) res += ask_t(ls, l, r);
    		if (r > mid) res += ask_t(rs, l, r);
    		return res;
    	}
    }st;
    
    int main(){
    	hand_in();
    	n = read(), c = read();
    	for (int i = 1;i <= n; ++i) {
    		mk[i].l = read(), mk[i].t = read();
    		rsd[i] = mk[i], ans += mk[i].l;
    	}
    	st.build(1, 1, 100001);
    	sort(mk + 1, mk + 1 + n, cmp);
    	for (int i = 1;i <= n; ++i) {
    		s += mk[i].t;
    		ans -= s;
    		st.change(1, mk[i].t, mk[i].t, 1);
    	}
    	printf("%lld
    ", ans);
    	for (int i = 1, id, l, t;i <= c; ++i) {
    		id = read(), l = read(), t = read();
    		ans -= rsd[id].l - st.ask_x(1, 1, rsd[id].t) - st.ask_t(1, rsd[id].t + 1, 100001) * rsd[id].t;
    		st.change(1, rsd[id].t, -rsd[id].t, -1);
    		rsd[id].l = l, rsd[id].t = t;
    		st.change(1, rsd[id].t, rsd[id].t, 1);
    		ans += rsd[id].l - st.ask_x(1, 1, rsd[id].t) - st.ask_t(1, rsd[id].t + 1, 100001) * rsd[id].t;
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    python 关于文件操作
    python2 编码与解码
    Git系列(二) 冲突解决
    异步回调机制分析
    CSS盒子模型理解
    Git多人协同开发模型
    CSS连载控制背景与CSS精灵
    函数调用在回调,委托与事件在程序设计中的应用
    TFS与Git结合进行代码管理
    Git系列之(二)Git协议与工作协同
  • 原文地址:https://www.cnblogs.com/silentEAG/p/11731968.html
Copyright © 2020-2023  润新知