2020,2021 年 CF 简单题精选 贺题记录
最近没什么事情,原神也开始每天20min上班模式
把之前收藏的题单好好做一下
CF1293 (Div. 2) D. Aroma's Search 1700pts..
solution
注意到 那么不难发现点从 一路向上走,并且跳的至少是之前两倍的距离
那么从起点 去搜集,找到一个最近的点 ,先从 往 走,然后如果有时间空余再像右上走,这个比较好发现
但是因为数据范围比较反常,我有点不知所措,就不会继续处理了
看了一个金钩爷的写法,一如既往的妙。。
主要是一点, 所以直接写一个 70 的 就行了
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pi acos(-1)
#define il inline
#define rg register
#define SZ(x) ((int)(x).size())
#define all(x) x.begin(),x.end()
#define INF 0x7fffffff;
#define inf 0x3f3f3f3f;
#define MOD 998244353;
#define mod 1000000007;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef long long ll;
typedef double db;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; }
const int N = 70;
ll xs, ys, ans, n, t;
ll x[N], y[N], ax, ay, bx, by;
ll dis(ll x1, ll y1, ll x2, ll y2) {
return llabs(x1 - x2) + llabs(y1 - y2);
}
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
cin >> x[0] >> y[0] >> ax >> ay >> bx >> by;
cin >> xs >> ys >> t;
while (++n) {
x[n] = ax * x[n-1] + bx;
y[n] = ay * y[n-1] + by;
if (x[n] > xs && y[n] > ys && dis(x[n], y[n], xs, ys) > t) break;
}
for (int i = 0; i <= n; i++) {
ll tans = 0, tt = t;
if (dis(xs, ys, x[i], y[i]) <= tt) tt -= dis(xs, ys, x[i], y[i]), tans++;
else {ans = max(ans, tans); continue;}
for (int j = i; j; j--) {
if (dis(x[j], y[j], x[j-1], y[j-1]) <= tt) {
tt -= dis(x[j], y[j], x[j-1], y[j-1]), tans++;
} else
break;
}
for (int j = 1; j <= n; j++) {
if (dis(x[j], y[j], x[j-1], y[j-1]) <= tt) {
tt -= dis(x[j], y[j], x[j-1], y[j-1]), tans += j > i;
} else
break;
}
ans = max(ans, tans);
}
cout << ans << endl;
return 0;
}
CF1304 (Div. 2) C. Air Conditioner 1500pts..
看了题目第一眼的想法是从后往前决定每一个 时刻温度应当偏低还是偏高 或者不动
然后看了一眼 感觉怪怪的 先写一遍再说吧
然后写的时候发现问题了,,,
应该是对于给定的时间 和当前能取到的范围 ,应该将 与下一个范围取交集,如果为空即无解
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pi acos(-1)
#define il inline
#define rg register
#define SZ(x) ((int)(x).size())
#define all(x) x.begin(),x.end()
#define INF 0x7fffffff;
#define inf 0x3f3f3f3f;
#define MOD 998244353;
#define mod 1000000007;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef long long ll;
typedef double db;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; }
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int q;
cin >> q;
while (q--) {
int n, m;
cin >> n >> m;
vector<int> d(n+1);
vector<pair<int,int>> line(n+1);
d[0] = 0, line[0].fi = line[0].se = m;
for (int i = 1; i <= n; i++)
cin >> d[i] >> line[i].fi >> line[i].se;
bool Flag = true;
for (int i = 1; i <= n; i++) {
line[i-1].fi -= (d[i]-d[i-1]);
line[i-1].se += (d[i]-d[i-1]);
line[i].fi = max(line[i].fi, line[i-1].fi);
line[i].se = min(line[i].se, line[i-1].se);
if (line[i].se < line[i].fi) {
// cout << line[i].fi << ' ' << line[i].se << endl;
Flag = false; break;
}
}
if (Flag) puts("YES");
else puts("NO");
}
return 0;
}
CF1313 (Div. 2) D. Happy New Year 2500pts
观察了一会儿,发现 最多连续的k个区间有交集,即按左端点排序之后,任意的
虽然k很小,但是之后就不会处理了...
题意:
给定 和 个区间
对于一个长为 的初始值为 的序列 ,在给定的 个区间中选择若干个,每个区间使得 中元素各 ,保证任何时刻 中的最大值
问序列中最多可能存在的奇数个数
solution
预处理的时候把每个区间的 和 存进去,区域拆成点来处理,显然只有这两类点处答案会有变化
从左往右枚举端点,把当前包含的区间放到 里面,然后这个 就莫名巧妙的滚动起来了
表示到第 个点,区间覆盖状态为 的最大答案
中二进制下如果第 位是 ,表示选择了 且 覆盖在当前端点上
表示该端点在数组 中的位置,相邻两点可以如下递推
分类讨论一下,然后代码能看懂,跟上面的式子好像有点出入,就前两个 的更新式子并不是依照 ,而是以之前能取到的所有状态
这题是真贺了快两天还没完全看懂题解,真麻了
以后状压DP做多了再来雪耻吧..
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pi acos(-1)
#define il inline
#define rg register
#define SZ(x) ((int)(x).size())
#define all(x) x.begin(),x.end()
#define INF 0x7fffffff;
#define inf 0x3f3f3f3f;
#define MOD 998244353;
#define mod 1000000007;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef long long ll;
typedef double db;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; }
const int N=100010,K=10;
int n,m,k,nn;
int l[N],r[N];
struct point{
int op,w,d;
point(int OP=0,int W=0,int D=0){op=OP,w=W,d=D;}
} p[N<<1];
bool cmp(point x,point y){
if(x.w==y.w) return x.op<y.op;
return x.w<y.w;
}
int d[K],one[260];
int f[N<<1][260];
void amax(int &a,int b){a=max(a,b);}
int dp(){
int all=(1<<k)-1;
rep(i,1,all)one[i]=one[i-(i&-i)]+1;
rep(i,0,nn) rep(j,0,all) f[i][j]=-inf;
f[0][0]=0;
rep(j,1,k) d[j]=-1;
int now=0;
rep(i,1,nn){
int at=-1;
if(p[i].op==1){
rep(j,1,k) if(d[j]==-1){
at=j,d[j]=p[i].d;break;
}
assert(~at);
rep(j,0,now) if((j&now)==j){
amax(f[i][j],f[i-1][j]+(one[j]&1)*(p[i].w-p[i-1].w));
amax(f[i][j|(1<<(at-1))],f[i-1][j]+(one[j]&1)*(p[i].w-p[i-1].w-1)+((one[j]+1)&1));
}
now|=(1<<(at-1));
} else{
rep(j,1,k) if(d[j]==p[i].d){at=j,d[j]=-1;break;}
assert(~at);
now^=(1<<(at-1));
rep(j,0,now) if((j&now)==j){
amax(f[i][j],f[i-1][j]+(one[j]&1)*(p[i].w-p[i-1].w));
amax(f[i][j],f[i-1][j|(1<<(at-1))]+((one[j]+1)&1)*(p[i].w-p[i-1].w-1)+(one[j]&1));
}
}
}
return f[nn][0];
}
int main(){
cin.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m>>k;
rep(i,1,n){
cin>>l[i]>>r[i];
p[++nn]=point(1,l[i],i);
p[++nn]=point(-1,r[i]+1,i);
}
sort(p+1,p+1+nn,cmp);
cout<<dp()<<endl;
return 0;
}
CF1323 (Div. 2) D. Present 2100pts..
给定长为 的序列 , 求出 所有的 的相异或的值
按位考虑每一位 ,对于第k位,每个数考虑 的部分
两个数的和要在 和 的范围内,那么第 位会是 ,所以排序之后统计一边即可
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector<int> a(n), b(n);
for (int i = 0; i < n; ++i)
std::cin >> a[i];
int ans = 0;
for (int k = 0; k < 25; ++k) {
for (int i = 0; i < n; ++i) {
b[i] = a[i] & ((1 << (k + 1)) - 1);
}
std::sort(b.begin(), b.end());
int d = 1 << k;
ll cnt = 0;
for (int i = n - 1, x = 0, y = 0, z = 0; i >= 0; --i) {
while (x < n && b[x] + b[i] < d)
++x;
while (y < n && b[y] + b[i] < d * 2)
++y;
while (z < n && b[z] + b[i] < d * 3)
++z;
cnt += std::max(0, std::min(i, y) - x);
cnt += std::max(0, i - z);
}
if (cnt & 1)
ans |= (1 << k);
}
std::cout << ans << '\n';
return 0;
}`
CF1322 (Div. 2) C. Instant Noodles
一开始往左半部的点去考虑了,然后发现 重复的点其实是不好处理gcd的..
然后用右半边的方式去看,因为辗转相减的原理,很容易得到
对于每个右半部的点,把与它关联的点放到它的集合里面,这些点可以合并,这个合并后的点集对于gcd的贡献就是右半部当前点的权值 因为之前那个式子,可以发现要考虑不同因数就是由各种左半部的点集决定的,合并之后搞一搞,复杂度还是可做的
#include <bits/stdc++.h>
using ll = long long;
template<typename T>
inline void scan(T &x) {
x=0; int f=1; char ch=getchar();
while(ch<'0'||ch>'9')
f=(ch=='-')?-1:1,ch=getchar();
while (ch>='0'&&ch<='9')
x=(x<<3)+(x<<1)+ch-48,ch=getchar();
x*=f;
}
template<typename T>
inline void _write(T x) {
if (x>=10) _write(x/10);
putchar('0'+x%10);
}
template<typename T>
inline void write(T x) {
if (x<0) putchar('-');
_write(x);
}
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
int main() {
int tt;
scan(tt);
while (tt--) {
int n, m;
scan(n), scan(m);
std::map<std::set<int>, ll> mp;
std::set<int>s[n+1];
std::vector<ll> tot(n + 1, 0LL);
for (int i = 1; i <= n; i++)
scan(tot[i]);
for (; m; --m) {
int u, v;
scan(u), scan(v);
s[v].insert(u);
}
for (int i = 1; i <= n; ++i) {
if (s[i].empty()) continue;
mp[s[i]] += tot[i];
}
ll ans = 0;
for (auto &it : mp) {
if (ans) ans = gcd(ans, it.second);
else ans = it.second;
}
write(ans);
puts("");
}
return 0;
}
CF1325 (Div. 2) D. Ehab the Xorcist
给定两个数
求一个最短的数组,所有值异或为 ,和为
如果没有方案输出-1
显然每个数必须大于0