【日记】12.23/【题解】CF Edu78
12.23日记
DP
- 洛谷P1280:工作日有N分钟,有K个任务,每个任务从\(p_i\)分钟开始,持续\(t_i\)分钟,每一时刻如果有多个任务要完成,则可以任选一个,但只能干一个工作,问如何选取任务,使得空暇时间最多。数据1e4。
思路:dp[i]表示i-N分钟中,最大的空闲时间。那么从后往前遍历每个任务,这个是无后效性的,与前面的任务怎么选是无关的。那么首先对任务开始时间从大到小排序,再依次从时间n-1开始dp即可。
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+20;
struct T{
int st,dur;
bool operator<(const T &x)const{
return st>x.st;
}
};
struct Task{
int n,k,dp[M];
T a[M];
void init(){
scanf("%d%d",&n,&k);
for(int i=1;i<=k;++i)
scanf("%d%d",&a[i].st,&a[i].dur);
}
void run(){
sort(a+1,a+k+1);
int p=1;
for(int i=n;i>=1;--i)
if (a[p].st!=i)
dp[i]=dp[i+1]+1;
else
while(a[p].st==i)
dp[i]=max(dp[i],dp[i+a[p].dur]),++p;
printf("%d\n",dp[1]);
}
}task;
int main(){
task.init(),task.run();
return 0;
}
CF Edu78
A. Shuffle Hashing
题意:给字符串s和h,s的hash定义为,将s随机打乱,之后加上一个任意前缀和一个任意后缀,得到的字符串即为s的hash,现询问h是否可以为s的hash。
思路:遍历所有h的长为len(s)的区间,统计各个字符个数,如果和s的各个字符个数一样,那么就是可以。时间复杂度\(O(n^2)\)。
#include<bits/stdc++.h>
using namespace std;
const int M=1e2+50;
struct T{
int ans[28],now[28],len1,len2;
char s1[M],s2[M];
void init(){
for(int i=0;i<26;++i)
ans[i]=now[i]=0;
scanf("%s%s",s1,s2);
len1=strlen(s1),len2=strlen(s2);
}
inline bool check(){
for(int i=0;i<26;++i)
if (ans[i]!=now[i])
return false;
return true;
}
void run(){
init();
if(len2<len1){
printf("NO\n");
return;
}
for(int i=0;i<len1;++i)
++ans[s1[i]-'a'],++now[s2[i]-'a'];
int p=len1;
while(p<len2&&!check())
--now[s2[p-len1]-'a'],++now[s2[p]-'a'],++p;
if (check())
printf("YES\n");
else
printf("NO\n");
}
}t;
int main(){
int T;
scanf("%d",&T);
for(int i=1;i<=T;++i)
t.run();
return 0;
}
B. A and B
题意:给两个数a和b,第k次操作为选定一个数,将其加上k。输出使得a和b相等的最少步数。
思路:求出a和b的差d,那么找到第一个x,使得x(x+1)/2>=d(也就是疯狂给那个小的数加),若d!=0,则此时a和b还是不一样。那么问题就转化为了,将x(x+1)/2分成两个数,使其相差d。这就要看x(x+1)/2+d是否为偶数,若为奇数,则还需要继续加,直到为偶数为止,这样才能分成整数。很容易理解一定加了不超过2次。
时间复杂度\(O(\log n)\)。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
struct T{
int a,b,d;
void init(){
scanf("%d%d",&a,&b);
d=abs(a-b);
}
bool check(int x){
if (1LL*x*(x+1)/2>=d)
return true;
return false;
}
void run(){
init();
int l=0,r=1e5+20;
while(l!=r){
if (check(mid))
r=mid;
else
l=mid+1;
}
while((1LL*l*(l+1)/2+d)%2==1)
++l;
printf("%d\n",l);
}
}t;
int main(){
int T;
scanf("%d",&T);
for(int i=1;i<=T;++i)
t.run();
return 0;
}
C.Berry Jam
题意:给2n个罐头,一开始站在n和n+1之间,每个罐头为1或者2,可以选择吃掉i-n和n+1-j两个区间内的所有罐头。问最少需要吃掉多少个罐头,才能使得剩下的1和2的数量相等。
题解:假设1个数>2个数,那么首先计算出要额外吃掉的1的个数,记为x。之后计算的时候碰到1就+1,碰到2就-1。首先计算n往左走,每走过一个就+1/-1。可以想到,如果i处为sum,j处也为sum,而且i<j,那么i一定没用。所以对于每个sum,只需要记录第一个出现的j即可。那么可以将出现的位置存入数组或者队列。对右边也一样处理。之后枚举左0右x,左1右x-1……左x右0,根据记录的位置计算出每种情况要吃的罐头的数量,取最小值即可,注意要处理左边可能没有x的情况。
时间复杂度\(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+20;
#define tr(x) (x==pd?1:-1)
struct pl{
int val,num;
pl(int a=0,int b=0):val(a),num(b){};
};
struct T{
int n,a[M*2],num[3],pd,dt,sum,ans;
void init(){
scanf("%d",&n);
num[1]=num[2]=0;
for(int i=1;i<=2*n;++i)
scanf("%d",&a[i]),++num[a[i]];
dt=abs(num[1]-num[2]),ans=1e9+7;
}
int run(){
init();
if (num[1]==num[2])
return 0;
if (num[1]>num[2])
pd=1;
else
pd=2;
deque<pl> l,r;
sum=0;
l.push_back(pl(0,0));
for(int i=n;i>=1;--i){
sum+=tr(a[i]);
if (sum>l.back().val)
l.push_back(pl(sum,n-i+1));
}
sum=0;
r.push_back(pl(0,0));
for(int i=n+1;i<=2*n;++i){
sum+=tr(a[i]);
if (sum>r.back().val)
r.push_back(pl(sum,i-n));
}
while(!l.empty()){
if (l.front().val>dt)
break;
while(r.back().val>dt-l.front().val)
r.pop_back();
if (r.back().val+l.front().val==dt)
ans=min(ans,l.front().num+r.back().num);
l.pop_front();
}
return ans;
}
}t;
int main(){
int T;
scanf("%d",&T);
for(int i=1;i<=T;++i)
printf("%d\n",t.run());
return 0;
}
D. Segment Tree
题意:给n个点,每个点表示一个区间\([l_i,r_i]\),保证所有的l,r均不重复。若两个区间有重叠但不包含,则两个点之间有边。问这个图是否是一颗树。
思路:这也算是一道数据结构题了,结果不会做………………
区间按照l从小到大排序,每读入一个区间就将右端点和编号放入set,那么放入之前,set里剩的元素就是与之有边的元素。如果set里有右端点太小的边就删掉。显然,如果发现了超过了n-1条边,那么就直接NO。如果恰好n-1条边,再判断连通性即可。可以用并查集。
时间复杂度\(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
const int M=5e5+20,P=1e9+7;
struct Seg{
int l,r,no;
Seg(int a=0,int b=0,int c=0):l(a),r(b),no(c){}
bool operator<(const Seg &x)const{
return r<x.r;
}
};
struct cmp{
bool operator()(Seg a,Seg b){
return a.l<b.l;
}
};
struct Task{
int n;
Seg a[M];
int road[M];
inline int fnd(int x){
return (x==road[x])?x:road[x]=fnd(road[x]);
}
inline void merge(int u,int v){
road[fnd(u)]=fnd(v);
}
void init(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d%d",&a[i].l,&a[i].r),a[i].no=i,road[i]=i;
}
void run(){
init();
sort(a+1,a+n+1,cmp());
set<Seg> s;
int ans=0;
for(int i=1;i<=n;++i){
set<Seg>::iterator it=s.begin();
while(it!=s.end()&&it->r<a[i].l)
it=s.erase(it);
while(it!=s.end()&&it->r<a[i].r){
++ans,merge(a[i].no,it->no),++it;
if (ans>n-1){
printf("NO\n");
return;
}
}
s.insert(a[i]);
}
if (ans!=n-1){
printf("NO\n");
return;
}
unordered_set<int> st;
for(int i=1;i<=n;++i)
st.insert(fnd(i));
if (st.size()==1)
printf("YES\n");
else
printf("NO\n");
}
}t;
int main(){
t.run();
return 0;
}