POI题目选做
[POI2015]WIL-Wilcze doły
题意
给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0。请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p。
题解
显然长度为D时只会更优,尺取法枚举,单调队列维护被删除的一段
#include<bits/stdc++.h> using namespace std; #define LL long long inline LL read() { LL 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; } const int MAXN = 2000000 + 5; LL n,p,d; LL sum[MAXN],a[MAXN]; LL ans; LL q[MAXN],head = 1,tail; inline LL calc(LL x) { return sum[x] - sum[x-d]; } int main() { n = read(),p = read(),d = read(); for(int i=1;i<=n;i++) a[i] = read(),sum[i] = sum[i-1] + a[i]; ans = d; q[++tail] = d; int l = 1; for(int i=d+1;i<=n;i++) { while(head <= tail && calc(i) > calc(q[tail])) tail--; q[++tail] = i; while(head <= tail && sum[i] - sum[l-1] - calc(q[head]) > p) { l++; while(head <= tail && l >q[head] - d + 1) head++; } ans = max(ans, 1LL * i - l + 1); } cout << ans << endl; }
[POI2012]LIT-Letters
题意
给出两个长度相同的的只含大写字母的字符串 a,b
每次可以交换相邻字符,求a到b的最小交换次数
题解
火柴排队弱化版?
显然逆序对
#include<bits/stdc++.h> using namespace std; #define LL long long inline LL read() { LL 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; } const int MAXN = 1000000 + 5; int n; char s[MAXN]; int a[MAXN]; queue<int>Q[30]; LL c[MAXN]; inline int lowbit(int x) { return x&(-x); } inline LL Query(LL x) { LL res = 0; while(x) { res += c[x]; x -= lowbit(x); } return res; } inline void update(int x,int v) { while(x <= n) { c[x] += v; x += lowbit(x); } } int main() { n = read(); scanf("%s",s+1); for(int i=1;i<=n;i++) a[i] = s[i] - 'A'; scanf("%s",s+1); for(int i=1;i<=n;i++) Q[s[i] -'A'].push(i); for(int i=1,cur;i<=n;i++) cur = a[i],a[i] = Q[cur].front(),Q[cur].pop(); LL ans = 0; for(int i=1;i<=n;i++) { ans += i - 1 - Query(a[i]); update(a[i],1); } cout << ans << endl; }
[POI2013]LUK-Triumphal arch
题意
给一颗树,1号节点已经被染黑,其余是白的,两个人轮流操作,一开始B在1号节点,A选择k个点染黑,然后B走一步,如果B能走到A没染的节点则B胜,否则当A染完全部的点时,A胜。
求能让A获胜的最小的k
题解
显然二分答案
考虑树形DP检验
显然只需要考虑再子树i中最多需要染几次色
把儿子不够的染色次数合并上来即可
#include<bits/stdc++.h> using namespace std; #define LL long long inline LL read() { LL 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; } const int MAXN = 3e5 + 10; int n,ans; vector<int>G[MAXN]; int dp[MAXN]; inline void dfs(int x,int f,int mid) { int sz = 0; for(int i=0;i<G[x].size();i++) { int v = G[x][i]; if(v == f) continue; dfs(v,x,mid); sz++; dp[x] += dp[v]; } dp[x] = max(0,dp[x]+sz-mid); } inline bool check(int mid) { memset(dp,0,sizeof(dp)); dfs(1,0,mid); if(dp[1] == 0) return true; else return false; } int main() { n = read(); for(int i=1;i<n;i++) { int u = read(),v =read(); G[u].push_back(v); G[v].push_back(u); } int l = 0,r = n; while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) { r = mid - 1; ans = mid; } else l = mid + 1; } cout << ans << endl; }