A - Remove Smallest
排个序,如果相邻的数大于一就不满足题意
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=110;
int a[N];
int main()
{IO;int T;cin>>T;while(T--){int n;cin>>n;for(int i=0;i<n;i++) cin>>a[i];sort(a,a+n);bool ok=1;for(int i=0;i<n-1;i++)if(a[i+1]>a[i]+1) {ok=0;break;}if(ok) cout<<"YES"<<endl;else cout<<"NO"<<endl;}return 0;
}
B - Gifts Fixing
礼物最终个数肯定是最小的那个。每个礼物减小到最少的那个,注意可以同时减少两种礼物。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=110;
ll a[N],b[N];
int main()
{IO;int T;cin>>T;while(T--){int n;cin>>n;ll mina=2e9;ll minb=2e9;for(int i=0;i<n;i++) {cin>>a[i];mina=min(mina,a[i]);}for(int i=0;i<n;i++){cin>>b[i];minb=min(minb,b[i]);}ll res=0;for(int i=0;i<n;i++) res+=max(a[i]-mina,b[i]-minb);cout<<res<<endl;}return 0;
}
C - Boats Competition
枚举+双指针
由于体重非常小,两两配对的体重和也很小,直接暴力枚举两两体重和w。现在问题转化成原序列找出两个数的和等于w,双指针。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=110;
int a[N],n;
int main()
{IO;int T;cin>>T;while(T--){cin>>n;for(int i=1;i<=n;i++) cin>>a[i];if(n==1) {cout<<0<<endl;continue;}sort(a+1,a+n+1);int res=0,mx=0;for(int w=a[1]+a[2];w<=a[n-1]+a[n];w++){int cnt=0;for(int i=1,j=n;i<j;i++){while(j>i&&a[i]+a[j]>w) j--;if(j<=i) break; //注意这点 因为这wa了一次if(a[i]+a[j]==w) cnt++,j--;}res=max(res,cnt);}cout<<res<<endl;}return 0;
}
D - Binary String To Subsequences
贪心+二分
贪心:如果该位是1那么找一个末尾是0的序列放进去如果不存在那么新添一组
比如目前有五组0 0 1 1 1
分别表示该组最后一个字符是0或者1,如果该位是1我们就二分出最后一个0,pos=2,组别表示变成0 1 1 1 1
。如果该位使0我们二分处第一个1的位置,pos=3,组别表示变成0 0 0 1 1
,这样操作可以发现组别表示的序列始终是有序的,保证时间复杂度为O(nlogn)O(nlogn)O(nlogn)。
好像有O(n)O(n)O(n)的做法奈何wctl,写题的时候只想到这个做法。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
const int N=200010;
int pos[N],a[N];
int main()
{IO;int T;cin>>T;while(T--){int n;string s;cin>>n>>s;int cnt=1;a[1]=s[0]-'0';pos[0]=1;for(int i=1;i<n;i++){int t=s[i]-'0';if(t){int l=1,r=cnt;while(l<r){int mid=l+r+1>>1;if(a[mid]<=0) l=mid;else r=mid-1;}if(a[l]==0){a[l]=1;pos[i]=l;}else {a[++cnt]=1;pos[i]=cnt;}}else{int l=1,r=cnt;while(l<r){int mid=l+r>>1;if(a[mid]>=1) r=mid;else l=mid+1;}if(a[l]==1){a[l]=0;pos[i]=l;}else {a[++cnt]=0;pos[i]=cnt;}}}cout<<cnt<<endl;for(int i=0;i<n;i++) cout<<pos[i]<<" ";cout<<endl;}return 0;
}
这次做了4个题,差一点做出了E1。
E1 - Weights Division (easy version)
我没想到这次我不在意的点就是这题的关键的!!!
先dfs一下把从根节点到每个叶子节点每条边跑的次数记录一下,记作cnt[i]
,如果该边边权为w[i]
如果把这条边砍一半,那么总的路程减小(w[i]-w[i]/2)*cnt[i]
,显然贪心,用个优先队列(按照减小的多优先级高)维护一下就行了。
坑点:很容易想到按照w[i]*cnt[i]
排序,不过这题下取整导致问题不断。(我做的时候想这题应该不会那么麻烦,就图个省事结果。。。)
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int h[N],e[M],ne[M],idx;
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{ll w;int num;bool operator <(const node&o)const{ll sub1=(w-w/2)*num;ll sub2=(o.w-o.w/2)*o.num;return sub1<sub2;//其实也可以直接 return (w+1)/2*num<(o.w+1)/2*o.num; 上去整}
};
priority_queue<node> q;void add(int a,int b,ll c)
{e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;d[b]++;//统计度数,度数为1是叶子节点
}
int dfs(int u,int fa)
{int lcnt=0;for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(j==fa) continue;cnt[i]+=(d[j]==1);cnt[i]+=dfs(j,u);lcnt+=cnt[i];}return lcnt;
}
void init()
{for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;idx=res=0;while(q.size()) q.pop();
}
int main()
{IO;int T;cin>>T;while(T--){cin>>n>>limit;init();for(int i=1;i<n;i++){int a,b;ll c;cin>>a>>b>>c;add(a,b,c);add(b,a,c);}dfs(1,-1);for(int i=0;i<2*n-2;i+=2) {q.push({w[i],cnt[i]+cnt[i+1]});res+=w[i]*(cnt[i]+cnt[i+1]);}ll ans=0;while(res>limit){auto t=q.top();q.pop();q.push({t.w/2,t.num});res-=1ll*(t.w-t.w/2)*t.num;ans++;}cout<<ans<<endl;}return 0;
}
E2 - Weights Division (hard version)
E1砍边都是花费1代价,而E2就是有些边砍一半需要花费1代价有些2代价。显然我们考虑用两个优先队列维护q1,q2
,q1
维护花费1代价的 q2
维护花费2代价的。仍然贪心。每次考虑花费1代价砍边,如果发现花两次1代价减小的路程小于直接花费2代价砍边减小的路程,那么就直接花费2代价砍边。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100010,M=200010;
int h[N],e[M],ne[M],idx,cost[M];
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{ll w;int num;bool operator <(const node&o)const{ll sub1=(w-w/2)*num;ll sub2=(o.w-o.w/2)*o.num;return sub1<sub2;}
};
priority_queue<node> q1,q2;
void add(int a,int b,ll c,int coin)
{e[idx]=b;w[idx]=c;cost[idx]=coin;ne[idx]=h[a];h[a]=idx++;d[b]++;
}
int dfs(int u,int fa)
{int lcnt=0;for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(j==fa) continue;cnt[i]+=(d[j]==1);cnt[i]+=dfs(j,u);lcnt+=cnt[i];}return lcnt;
}
void init()
{for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;idx=res=0;while(q1.size()) q1.pop();while(q2.size()) q2.pop();q1.push({0,0}),q1.push({0,0}),q2.push({0,0});//哨兵,避免很多边界情况。
}
int main()
{IO;int T;cin>>T;while(T--){cin>>n>>limit;init();for(int i=1;i<n;i++){int a,b,coin;ll c;cin>>a>>b>>c>>coin;add(a,b,c,coin);add(b,a,c,coin);}dfs(1,-1);for(int i=0;i<2*n-2;i+=2) {if(cost[i]==1) q1.push({w[i],cnt[i]+cnt[i+1]});else q2.push({w[i],cnt[i]+cnt[i+1]});res+=w[i]*(cnt[i]+cnt[i+1]);}ll ans=0;while(res>limit){auto a=q1.top();q1.pop();auto b=q1.top();q1.pop();auto c=q2.top();q2.pop();ll suba=(a.w-a.w/2)*a.num;ll subb=max((a.w/2-a.w/4)*a.num,(b.w-b.w/2)*b.num);ll subc=(c.w-c.w/2)*c.num;if(res-suba<=limit) {ans++;break;}else {if(suba+subb<=subc){res-=subc;q1.push(a),q1.push(b);q2.push({c.w/2,c.num});ans+=2;}else{res-=suba;q1.push(b),q1.push({a.w/2,a.num});q2.push(c);ans++;}}}cout<<ans<<endl;}return 0;
}
注意:每次只考虑花费1代价砍边
我在写的时候写了一份每次考虑花费2代价砍边。比较两次1代价和一次2代价减小的路程吗,这样贪心其实不正确,非常dt。
下面代码是错误的贪心思路每次考虑花费2代价砍边
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int h[N],e[M],ne[M],idx,cost[M];
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{ll w;int num;bool operator <(const node&o)const{ll sub1=(w-w/2)*num;ll sub2=(o.w-o.w/2)*o.num;return sub1<sub2;}
};
priority_queue<node> q1,q2;void add(int a,int b,ll c,int coin)
{e[idx]=b;w[idx]=c;cost[idx]=coin;ne[idx]=h[a];h[a]=idx++;d[b]++;
}
int dfs(int u,int fa)
{int lcnt=0;for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(j==fa) continue;cnt[i]+=(d[j]==1);cnt[i]+=dfs(j,u);lcnt+=cnt[i];}return lcnt;
}
void init()
{for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;idx=res=0;while(q1.size()) q1.pop();while(q2.size()) q2.pop();
}
int main()
{IO;int T;cin>>T;while(T--){cin>>n>>limit;init();for(int i=1;i<n;i++){int a,b,coin;ll c;cin>>a>>b>>c>>coin;add(a,b,c,coin);add(b,a,c,coin);}dfs(1,-1);for(int i=0;i<2*n-2;i+=2) {if(cost[i]==1) q1.push({w[i],cnt[i]+cnt[i+1]});else q2.push({w[i],cnt[i]+cnt[i+1]});res+=w[i]*(cnt[i]+cnt[i+1]);}q1.push({0,0}),q1.push({0,0}),q2.push({0,0});ll ans=0;while(res>limit){auto a=q1.top();q1.pop();auto b=q1.top();q1.pop();auto c=q2.top();q2.pop();ll suba1=(a.w-a.w/2)*a.num;ll suba2=(a.w/2-a.w/4)*a.num;ll subb=(b.w-b.w/2)*b.num;ll subc=(c.w-c.w/2)*c.num;if(res-suba1<=limit) {ans++;break;}else if(res-suba1-max(suba2,subb)<=limit||res-subc<=limit) {ans+=2;break;}else if(res-suba1-subc<=limit) {ans+=3;break;}else {if(suba2>subb){if(suba1+suba2>subc){res-=suba1+suba2;q1.push(b),q1.push({a.w/4,a.num});q2.push(c);}else{res-=subc;q1.push(a),q1.push(b);q2.push({c.w/2,c.num});}}else{if(suba1+subb>subc){res-=suba1+subb;q1.push({b.w/2,b.num}),q1.push({a.w/2,a.num});q2.push(c);}else{res-=subc;q1.push(a),q1.push(b);q2.push({c.w/2,c.num});}}ans+=2;}}cout<<ans<<endl;}return 0;
}
嘻嘻还是div3能上一点分QvQ,希望今天div2掉少点分(div2日常掉分)-。-,要加油哦~