Solved
- Problem A. Coffee Break
- Problem B. Glider
- Problem C. Bacteria
- Problem D. Masquerade strikes back
- Problem E. Painting the Fence
- Problem F. Tickets
- Problem G. Tree Reconstruction
- Problem H. Theater Square
- Problem I. Heist
- Problem J. Buying a TV Set
- Problem K. Medians and Partition
- Problem L. Ray in the tube
- Summary
Solution
Problem A. Coffee Break
- 贪心,对需求时间进行排序。
- 使用优先队列取出当前所有天的最晚时刻的最小值,如果没有一天是满足条件的,则在重新开一天,否则加入那一天即可。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e6+10;
struct node{
ll a;
int id;
}s[maxn];
bool cmp1(node x,node y)
{
return x.a<y.a;
}
ll ans[maxn];
typedef pair<ll,ll> pre;
struct cmp{
bool operator ()(const pre p1,const pre p2){
return p1.second>p2.second;
}
};
ll num[maxn];
int main(){
priority_queue< pre,vector<pre>,cmp >q;
ll n,m,d;
cin>>n>>m>>d;
for(int i=1;i<=n;i++){
scanf("%lld",&s[i].a);
s[i].id=i;
}
sort(s+1,s+n+1,cmp1);
q.push(make_pair(1,s[1].a));
ans[s[1].id]=1;
int day=1;
for(int i=2;i<=n;i++){
//cout<<q.top().first<<endl;
if(q.top().second+d+1>s[i].a){
day++;
q.push(make_pair(day,s[i].a));
ans[s[i].id]=day;
}else{
int dd=q.top().first;
q.pop();
ans[s[i].id]=dd;
q.push(make_pair(dd,s[i].a));
}
}
printf("%d
",day);
for(int i=1;i<=n;i++){
printf("%lld%c",ans[i],"
"[i==n]);
}
}
Problem B. Glider
- 为了使飞机平滑的时间尽量多,那么每次飞机的起点一定是某个可平滑的线段的 (l) 段,高度一定是最大高度。
- 然后我们就可以通过枚举飞机的起点,去求出飞机的飞行距离。
- 由于是取一些平滑线段,想到可以用取尺法。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e6+10;
ll l[maxn],r[maxn];
map<ll,ll>sum;
int main()
{
int n;
ll h;
scanf("%d %lld",&n,&h);
for(int i=1;i<=n;i++){
scanf("%lld %lld",&l[i],&r[i]);
l[i]+=1e9;
r[i]+=1e9;
}
sum[l[1]]=0;
sum[r[1]]=0;
for(int i=2;i<=n;i++){
sum[l[i]]=-(l[i]-r[i-1]);
sum[r[i]]=sum[l[i]];
//cout<<"xx"<<sum[l[i]]<<" "<<sum[r[i]]<<endl;
}
ll l1=0,r1=0;
ll ans=0;
ll sumx=h;
ll num=0;
for(l1=1,r1=1;l1<=n;l1++){
if(l1>1){
sumx+=(l[l1]-r[l1-1]);
//num+=r[r1]-l[r1-1];
num=r[r1]-l[l1];
}
//cout<<"xx"<<sumx<<endl;
while(r1<n&&sumx>0){
r1++;
if(sumx+sum[l[r1]]<=0){
r1--;
//cout<<sumx<<" "<<num<<endl;
ans=max(ans,num+sumx);
//cout<<l1<<" "<<r1<<" "<<ans<<endl;
break;
}
num=r[r1]-l[l1];
//cout<<l1<<" "<<num<<endl;
sumx+=sum[l[r1]];
//cout<<"xx"<<sum[r[r1]]<<endl;
}
//cout<<"xx"<<num<<endl;
//cout<<l1<<" "<<r1<<endl;
if(sumx>0){
if(n==1){
ans=max(ans,r[1]-l[1]+sumx);
}
//cout<<sumx<<" "<<num<<endl;
ans=max(ans,num+sumx);
//cout<<l1<<" "<<r1<<" "<<ans<<endl;
}
}
cout<<ans<<endl;
}
Problem C. Bacteria
- 暴力解决或者找规律即可。
代码:
ll ok(ll x){
ll y=1;
while(1){
//cout<<y<<endl;
if(x>=y){
y*=2;
}
else{
break;
}
}
return y/2;
}
ll a[200005];
void run()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+1+n);
int pp = a[1];
for(int i=1;i<=n;i++)
{
int xx = a[i]/pp;
if(a[i]%a[1]!=0)
{
puts("-1");
return;
}
while(xx%2==0)
{
xx/=2;
}
//cout<<xx<<endl;
if(xx!=1)
{
puts("-1");
return;
}
a[i] = a[i]/pp;
}
ll sum = 0;
for(int i=1;i<=n;i++)
{
sum+=a[i];
}
ll xx = 1;
while(xx<sum)
{
xx*=2;
}
ll ans = xx - sum;
// cout<<ans<<endl;
ll yy=xx/2;
ll num=0;
while(ans>1){
//cout<<ok(ans)<<endl;
ans-=ok(ans);
//cout<<ans<<endl;
num++;
}
// cout<<num<<endl;
if(ans==1) num++;
cout<<num<<endl;
}
signed main()
{
//std::ios::sync_with_stdio(false);
run();
}
Problem D. Masquerade strikes back
- 对于某个数,每次找到一组两两不相等就代表这个数有两对可以符合。
- 那么每次只需要通过暴力寻找可以整除某个数的数。
- 复杂度 (O(n imes sqrt(10000000)))
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e6+10;
struct node{
int x,y;
}ans[maxn];
int num1[10000000+10];
int num2[10000000+10];
int c[maxn];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
num1[c[i]]=1;
}
int k=1;
for(int i=1;i<=n;i++){
if(num2[c[i]]>0){
ans[i].x=num2[c[i]];
ans[i].y=c[i]/num2[c[i]];
if(num2[c[i]]*num2[c[i]]!=c[i]&&num2[c[i]]<c[i]/num2[c[i]]){
num2[c[i]]=c[i]/num2[c[i]];
}else{
num2[c[i]]=0;
}
}else{
while(1){
if(num1[c[i]]*num1[c[i]]>c[i]){
k=0;
break;
}else{
if(c[i]%num1[c[i]]==0){
num2[c[i]]=num1[c[i]];
num1[c[i]]++;
break;
}else{
num1[c[i]]++;
}
}
}
if(k==0)break;
i--;
continue;
}
}
if(k){
printf("YES
");
for(int i=1;i<=n;i++){
printf("%d %d
",ans[i].x,ans[i].y);
}
}else{
printf("NO
");
}
}
Problem E. Painting the Fence
- 首先我们用 (set) 维护某个颜色的位置。
- 当我们需要操作某个颜色是,那么我们需要涂色的区域就是 (set) 的最大值和最小值,即 (*st.begin()) ~ (*st.rbegin())。
- 我们枚举这个区间,如果发现这个区间内出现了被操作过的区间,那么我们就可以直接跳到这个被操作过区间的末尾即可。否则就把原本这位置上的颜色的 (set) 删除这个位置。
- 在染色完以后标记已经染过的颜色即可。
- 每个点最多被访问两次,复杂度 (O(nlog(n)))
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
set<int>st[maxn];
int col[maxn];
int n,m;
int ans[maxn];
int vis[maxn];
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&col[i]);
st[col[i]].insert(i);
}
cin>>m;
for(int i=1;i<=m;i++){
int temp;
scanf("%d",&temp);
if(st[temp].size()<2||vis[temp]){
vis[temp]=1;
continue;
}
int l=*(st[temp].begin()),r=*(st[temp].rbegin());
for(int j=l+1;j<r;j++){
st[col[j]].erase(j);
if(vis[col[j]]&&st[col[j]].size()>=1){
j=*(st[col[j]].rbegin());
st[col[j]].erase(j);
}
}
vis[temp]=1;
}
for(int i=1;i<=maxn-5;i++){
if(st[i].size()!=0&&vis[i])
for(int j=*(st[i].begin());j<=*(st[i].rbegin());j++){
col[j]=i;
}
}
for(int i=1;i<=n;i++){
printf("%d%c",col[i],"
"[i==n]);
}
}
Problem F. Tickets
- 枚举 (0) ~ (999999) 求出前三位和减去后三位和的绝对值,然后每次记录这些绝对值,然后遍历到哪个数,就可以求出对于那个数的答案。
代码:
int sum(int num)
{
int x1=0,x2=0,pos=1;
while(num>0){
int x=num%10;
num/=10;
if(pos<=3){
x1+=x;
}else{
x2+=x;
}
pos++;
}
return abs(x2-x1);
}
int a[maxn];
int p[maxn];
void init()
{
for(int i=0;i<=999999;i++){
for(int j=0;j<sum(i);j++){
p[i]+=a[j];
}
a[sum(i)]++;
}
}
void run()
{
init();
int num;
int t = rd();
while(t--)
{
scanf("%d",&num);
printf("%d
",p[num]);
}
}
signed main()
{
//std::ios::sync_with_stdio(false);
run();
}
Problem G. Tree Reconstruction
- 首先考虑权值最大的点,对于每条边,一定有一个值等于 (n) 。
- 考虑构成一条链。
- 将每条边的值变成前面小、后面大的,后面大值可以保证一定是 (n)。
- 然后将每条边按小权值从大到小排序,从前往后遍历。
- 如果遇到当前权值被使用了,就去找一个没有被使用的较小的权值,否则就没有答案。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
struct node{
int l,r;
}s[1005];
bool cmp(node a,node b)
{
return a.l<b.l;
}
int have[1005];
vector<int>v;
int main()
{
int n;
cin>>n;
int k=1;
for(int i=1;i<n;i++){
scanf("%d%d",&s[i].l,&s[i].r);
if(s[i].l>s[i].r)swap(s[i].l,s[i].r);
if(max(s[i].l,s[i].r)!=n){
k=0;
}
}
sort(s+1,s+n,cmp);
if(k==0){
printf("NO
");
return 0;
}
int maxx;
for(int i=1;i<n;i++){
if(have[s[i].l]==0){
v.push_back(s[i].l);
have[s[i].l]++;
}else{
for(int j=1;j<=n;j++){
if(have[j]==0){
if(j>s[i].l){
k=0;
break;
}
v.push_back(j);
have[j]++;
break;
}
}
}
if(k==0)break;
}
v.push_back(n);
have[n]++;
for(int i=1;i<=n;i++){
if(have[i]==0||have[i]>1){
k=0;
break;
}
}
if(k==0||v.size()!=n){
printf("NO
");
}else{
printf("YES
");
for(int i=0;i<n-1;i++){
printf("%d %d
",v[i],v[i+1]);
}
}
}
Problem H. Theater Square
- 考虑部分被覆盖的那几行中,前面部分和后面部分奇偶性,以及列数的奇偶性得出答案。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e6+10;
int main()
{
int n,m,sum=0;
int x1,y1,x2,y2;
cin>>n>>m;
cin>>x1>>y1>>x2>>y2;
int d=(x2-x1+1);
if(y1%2==0)sum+=d;
if((m-y2)%2==1)sum+=d;
if(m%2==1){
sum+=(n-d);
}
printf("%d
",sum/2+(sum%2));
}
Problem J. Buying a TV Set
- 先约分,然后取比例最小的得到答案即可。
- 队友的二分答案也可以。
void run()
{
ll a,b,x,y;
cin>>a>>b>>x>>y;
ll ans = __gcd(x,y);
ll xx = x/ans;
ll yy = y/ans;
ll maxx1 = a/xx,maxx2 = b/yy;
if(min(maxx1,maxx2) == 0)
{
puts("0");
return;
}
ll left = 0,right = min(maxx1,maxx2);
ll maxx = 0;
while(left <= right)
{
ll mid = (left + right)>>1;
if(xx*mid<=a&&yy*mid<=b)
{
left = mid + 1;
maxx = max(maxx,mid);
}
else
{
right = mid - 1;
}
}
cout<<maxx<<endl;
}
signed main()
{
//std::ios::sync_with_stdio(false);
run();
}
Problem K. Medians and Partition
- 考虑满足条件的子数组一定是大于等于 (m) 的数比小于 (m) 的数至少多一个。
- 那么我们求出大于等于 (m) 的数为 (a)个,小于 (m) 的数为 (b) 个,那么答案就是 (max(0,a-b))。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m,k;
scanf("%d%d ",&n,&m);
int a=0,b=0;
while(n--){
scanf("%d",&k);
if(k>=m) a++;
else b++;
}
printf("%d
",max(a-b,0));
}
Problem L. Ray in the tube
- 考虑暴力,对每种距离进行枚举,然后求出答案。但这样暴力一定超时。
- 考虑每种距离一定由 (2^n) ((0<=n<=MAXN))组成 ,那么枚举 (2^n)即可。
- 时间复杂度 (O(nlog(1e9)))。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
int x1[maxn],x2[maxn];
map<int,int>mp;
int main()
{
int n,m,y1,y2;
mp.clear();
scanf("%d%d",&n,&y1);
for(int i=1;i<=n;i++){
scanf("%d",&x1[i]);
}
scanf("%d%d",&m,&y2);
for(int i=1;i<=m;i++){
scanf("%d",&x2[i]);
}
int ans=2;
for(int d=2;d<=1e9;d*=2){
mp.clear();
for(int i=1;i<=n;i++){
mp[x1[i]%d]++;
ans=max(ans,mp[x1[i]%d]);
}
for(int i=1;i<=m;i++){
mp[(x2[i]+d/2)%d]++;
ans=max(ans,mp[(x2[i]+d/2)%d]);
}
}
printf("%d
",ans);
}
Summary
**
- 在前期队友的题上机有错的情况下,再上去一个人一起看。
- 在卡题时,需要有人去开别的可做的题。
- 中期,最好由两个人去一起解一道题,整个队伍都需要明白题意,尽量减少一个人去开一道非打卡题的题目,除非很有把握。
- 后期增强配合。
2021-03-05 team training