codeforces-1132 (div2)

时间:2023-03-09 00:12:59
codeforces-1132 (div2)

A.发现b的个数没有意义,a不等于d一定不可行,c不管多少都算一个,如果只有c没有ad也不可行

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
int main(){
LL a,b,c,d;
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
c = min(c,1ll);
if((!a & !d) && c) puts("");
else if(a != d) puts("");
else puts("");
return ;
}

A

B.显然求出所有的和,排序之后,查询的时候减去K大的值就可以了。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = 3e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
LL a[maxn];
bool cmp(LL a,LL b){
return a > b;
}
int main(){
Sca(N);
LL sum = ;
for(int i = ; i <= N; i ++){
Scl(a[i]);
sum += a[i];
}
sort(a + ,a + + N,cmp);
int q = read();
while(q--){
int t = read();
printf("%lld\n",sum - a[t]);
}
return ;
}

B

C.显然可以先算出每个编号的墙被刷的数量以及全刷的话刷的总数,然后枚举两面不刷的墙,减去他们的贡献即可。

唯一的问题在于O(1)求出两面墙不刷之后刷不上东西的墙,用两个前缀和分别维护只刷了一发的墙和只刷了两发的墙,分类讨论即可。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
int a[maxn];
PII P[maxn];
int pre1[maxn],pre2[maxn];
bool cmp(PII a,PII b){
return a.fi < b.fi;
}
int main(){
Sca2(N,M);
for(int i = ; i <= M ; i ++){
int l = read(),r = read();
P[i].fi = l,P[i].se = r;
for(int j = l; j <= r; j ++) a[j]++;
}
int ans = ,sum = ;;
for(int i = ; i <= N ; i ++){
pre1[i] = pre1[i - ]; pre2[i] = pre2[i - ];
if(a[i] == ) pre1[i]++;
if(a[i] == ) pre2[i]++;
if(a[i]) sum++;
}
sort(P + ,P + + M,cmp);
for(int i = ; i <= M ; i ++){
for(int j = i + ; j <= M ; j ++){
int flag = ;
if(P[i].se < P[j].fi){
flag = pre1[P[i].se] - pre1[P[i].fi - ] + pre1[P[j].se] - pre1[P[j].fi - ];
}else if(P[j].se < P[i].se){
flag = pre2[P[j].se] - pre2[P[j].fi - ] + pre1[P[j].fi - ] - pre1[P[i].fi - ] + pre1[P[i].se] - pre1[P[j].se];
}else{
flag = pre1[P[j].fi - ] - pre1[P[i].fi - ] + pre2[P[i].se] - pre2[P[j].fi - ] + pre1[P[j].se] - pre1[P[i].se];
}
ans = max(ans,sum - flag);
}
}
Pri(ans);
return ;
}

C

D.二分 + check,check里面一个优先队列的小贪心,时间复杂度O(n * (logn) * (logn) )

结果竟然TLE了,一度想找O(n)的check但是因为菜放弃了。

后来发现是在堆比较函数里,形如a.a / a.b < b.a / b.b这样的操作会提高计算的常数,需要提前计算好答案在比较

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
LL read(){LL x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = 2e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
struct Node{
LL a,b,day;
Node(){}
Node(LL a,LL b,LL day):a(a),b(b),day(day){}
inline friend bool operator < (Node a,Node b){
return a.day > b.day;
}
}node[maxn];
inline bool check(LL x){
priority_queue<Node>Q;
for(register int i = ; i <= N ; i ++) Q.push(node[i]);
for(register int i = ; i < K ;i ++){
Node u = Q.top(); Q.pop();
u.a += x; u.day = u.a / u.b;
Q.push(u);
if(Q.top().day < i) return false;
}
return true;
}
LL solve(){
LL l = ,r = 1ll * K * 1e7 + K;
LL ans = -;
while(l <= r){
LL m = l + r >> ;
if(check(m)){
r = m - ;
ans = m;
}else{
l = m + ;
}
}
return ans;
}
inline bool cmp(Node a,Node b){
return a.a / a.b < b.a / b.b;
}
int main(){
Sca2(N,K);
for(register int i = ; i <= N ; i ++) node[i].a = read();
for(register int i = ; i <= N ; i ++){
node[i].b = read();
node[i].day = node[i].a / node[i].b;
}
sort(node + ,node + + N,cmp);
Prl(solve());
return ;
}

D

E.感觉像熟悉的背包问题,但是物品的数量和背包的容量大的夸张,所以需要用其他的方法。

对于数量很多这个情况,可以考虑像背包的二进制优化一样,变为很多更大的小物品并且不影响可以组成的容量种类。

由于背包的容量很大,所以从寻常的迭代查找变成了dfs + 剪枝搜寻的方法,用dfs(i,j)表示当前容量为i并且用到了前j个物品,map<pair<int,LL>,bool>作为vis标记去重之后,再用维护一个物品体积的后缀和方便dfs结束。

虽然这还是TLE了,但是我把所有物品体积从大到小sort了一发就过了

(看了题解似乎有更科学简单的做法?嗯???)

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = 2e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
LL W;
map<LL,LL>P;
LL S[maxn],pre[maxn],ans;
int top;
map<pair<LL,int>,bool>vis;
bool cmp2(LL a,LL b){
return a > b;
}
void dfs(LL t,int p){
if(vis[mp(t,p)]) return;
vis[mp(t,p)] = ;
if(p == top){
ans = max(ans,t);
return;
}
if(t + pre[p + ] <= W){
ans = max(ans,t + pre[p + ]);
return;
}
if(t + S[p + ] <= W) dfs(t + S[p + ],p + );
dfs(t,p + );
}
int main(){
Scl(W);
top = ;
priority_queue<LL,vector<LL>,greater<LL> >Q;
for(int i = ; i <= ; i ++){
LL x; Scl(x); P[i] = x;
if(x) Q.push(i);
}
while(!Q.empty()){
LL u = Q.top(); Q.pop();
LL n = P[u] * u;
P[u] = ;
LL now = u;
while(n >= now){
P[now]++;
if(P[now] == && now > u) Q.push(now);
n -= now;
now <<= ;
}
P[n]++;
if(n > u && P[n] == ) Q.push(n);
while(P[u]--) S[++top] = u;
}
sort(S + ,S + + top,cmp2);
for(int i = top; i >= ; i --){
pre[i] = S[i] + pre[i + ];
}
dfs(,);
Prl(ans);
return ;
}

E

F.感觉很像区间dp的一道题,实际上确实是区间dp,比较常规的dp[l][r]维护区间内的操作次数最小值。

比较麻烦的是区间的更新,很显然扩展的时候如果扩展出去的恰好和两端相等就不需要增加操作次数。

其次如果两端相等,可以从用中间len - 2长度区间 + 1的次数更新。

当然少不了长区间由左区间合并右区间的操作,显然如果交界处相等,更新答案是两者相加的答案 - 1

到了这一步事实上还过不了第一个样例abaca

需要增加一步,如果l,r之间有一个元素str[i]和str[r]相等,答案更新dp[l][i] + dp[i + 1][r - 1] + dp[r][r] - 1;

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
char str[maxn];
int dp[maxn][maxn];
int main(){
Sca(N);
scanf("%s",str + );
for(int i = ; i <= N ; i ++) dp[i][i] = ;
for(int len = ; len <= N ; len++){
for(int l = ; l + len - <= N; l ++){
int r = l + len - ;
dp[l][r] = INF;
if(str[r] == str[r - ]) dp[l][r] = min(dp[l][r],dp[l][r - ]);
else dp[l][r] = min(dp[l][r],dp[l][r - ] + );
if(str[l] == str[l + ]) dp[l][r] = min(dp[l][r],dp[l + ][r]);
else dp[l][r] = min(dp[l][r],dp[l + ][r] + );
if(str[l] == str[r]) dp[l][r] = min(dp[l][r],dp[l + ][r - ] + );
for(int k = l; k < r; k ++){
if(str[k] == str[k + ]) dp[l][r] = min(dp[l][r],dp[l][k] + dp[k + ][r] - );
else dp[l][r] = min(dp[l][r],dp[l][k] + dp[k + ][r]);
if(str[k] == str[r]) dp[l][r] = min(dp[l][r],dp[l][k] + dp[k + ][r - ]);
}
}
}
Pri(dp[][N]);
return ;
}

F

G.题意不是很容易懂,反正就是求所有K长度区间内的最长贪心子序列,最长贪心子序列的定义是每个元素后接所有第一个比他大的元素。

显然dp[i]表示从i出发的最长贪心子序列。手动模拟一下更新的样例,会发现在j位置更新,就是将k + 1 ~ j 位置的dp[i]全部++;k是左边第一个dp[k]大于等于dp[j]的位置。

那就线段树维护几个区间最大值,再区间更新一下就好了。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = 1e6 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
int a[maxn];
struct Tree{
int l,r,lazy;
int Max,dp;
}tree[maxn << ];
void Build(int t,int l,int r){
tree[t].l = l; tree[t].r = r;
tree[t].lazy = ;
if(l == r){
tree[t].Max = a[l];
tree[t].dp = ;
return;
}
int m = l + r >> ;
Build(t << ,l,m); Build(t << | ,m + ,r);
tree[t].dp = max(tree[t << ].dp,tree[t << | ].dp);
tree[t].Max = max(tree[t << ].Max,tree[t << | ].Max);
}
int query(int t,int l,int r,int p){
if(tree[t].l == tree[t].r){
if(tree[t].Max < p) return ;
return tree[t].l;
}
if(l <= tree[t].l && tree[t].r <= r){
if(tree[t << | ].Max >= p) return query(t << | ,l,r,p);
else return query(t << ,l,r,p);
}
int m = (tree[t].l + tree[t].r) >> ;
if(r <= m) return query(t << ,l,r,p);
else if(l > m) return query(t << | ,l,r,p);
else{
int ans = query(t << | ,m + ,r,p);
if(ans) return ans;
return query(t << ,l,m,p);
}
}
void Pushdown(int t){
if(tree[t].lazy){
tree[t << ].lazy += tree[t].lazy;
tree[t << | ].lazy += tree[t].lazy;
tree[t << ].dp += tree[t].lazy;
tree[t << | ].dp += tree[t].lazy;
tree[t].lazy = ;
}
}
void update(int t,int l,int r){
if(l <= tree[t].l && tree[t].r <= r){
tree[t].lazy++;
tree[t].dp++;
return ;
}
Pushdown(t);
int m = (tree[t].l + tree[t].r) >> ;
if(r <= m) update(t << ,l,r);
else if(l > m) update(t << | ,l,r);
else{
update(t << ,l,m);
update(t << | ,m + ,r);
}
tree[t].dp = max(tree[t << ].dp,tree[t << | ].dp);
}
int query(int t,int l,int r){
if(l <= tree[t].l && tree[t].r <= r) return tree[t].dp;
Pushdown(t);
int m = (tree[t].l + tree[t].r) >> ;
if(r <= m) return query(t << ,l,r);
else if(l > m) return query(t << | ,l,r);
else return max(query(t << ,l,m),query(t << | ,m + ,r));
}
int main(){
Sca2(N,K);
for(int i = ; i <= N ; i ++) Sca(a[i]);
Build(,,N);
for(int i = ; i <= K - ; i ++){
int p = query(,,i - ,a[i]);
// cout << p + 1 << " " << i - 1 << endl;
if(p + <= i - ) update(,p + ,i - );
}
for(int i = K; i <= N ; i ++){
if(K != ){
int p = query(,i - K + ,i - ,a[i]);
if(!p) p = i - K;
// cout << p + 1 << " " << i - 1 << endl;
if(p + <= i - ) update(,p + ,i - );
}
printf("%d ",query(,i - K + ,i));
}
return ;
}

G