#521div3
https://codeforces.com/contest/1077
#53div2-C
https://codeforces.com/contest/1073/problem/C
进入正文
521B
找到使得101不存在的最少改变序列。
就是离线处理这个序列,贪心的遇到101就改掉第二个1为0,然后一直On查找至结束
int t;
cin>>t;
n=0;
for(int i=0;i<t;i++){
cin>>sumx[i];
if(i>1&&sumx[i]==1&&sumx[i-2]==1&&sumx[i-1]==0){
n++;
sumx[i]=0;
}
}
cout<<n<<endl;
521C
去掉某个序列中的值,可以使得剩下的序列中有一个值,等于,剩下序列和的一半。判断sum-a[i]是不是2的倍数,然后判断剩下的序列里存不存在(sum-a[i])/2。
ll n,m,x,y;
int sumx[maxm+5];
map<ll,int>sumy;
vector<int>que;
int main(){
ios::sync_with_stdio(false);
int t;
cin>>t;
n=0;
sumy.clear();
for(int i=0;i<t;i++){
cin>>sumx[i];
sumy[sumx[i]]++;
n+=sumx[i];
}
que.clear();
for(int i=0;i<t;i++){
m=n-sumx[i];
if(m&1)continue;
m=m/2;
if(sumy[m]>0){
if(m==sumx[i]){
if(sumy[m]>1)que.push_back(i+1);
}
else que.push_back(i+1);
}
}
cout<<que.size()<<endl;
for(int i=0;i<que.size();i++){
cout<<que[i]<<" ";
}
return 0;
}
521D
我提出了个假算法,被hack了,见太少,不知道这个是二分。。。。
二分是对单调序列,这里是二分出符合条件的最大次数,然用这个次数去check,判断是否满足K个
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define mem(a,b) memset(a,b,sizeof a)
const int mod=1e9+7;
const int maxn=2e5;//3个确定 1e15
int a[maxn+5],b[maxn+5];
ll n,k;
bool check(ll mid){
int sum=0;
for(int i=1;i<=maxn;i++){
sum+=b[i]/mid;
}
return (sum>=k);
}
int main() {
int t;
cin>>n>>k;
mem(b,0);
for(int i=0;i<n;i++){
cin>>a[i];
b[a[i]]++;
}
ll l=1,r=n;
ll mid;
ll temp,ans;
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
ans=mid;
l=mid+1;
}
else r=mid-1;
}
//cout<<ans<<endl;
vector<int>q;
q.clear();
ll sum=0;
for(int i=1;i<=maxn;i++){
while(b[i]>=ans&&sum<k){
b[i]=b[i]-ans;
q.push_back(i);
sum++;
}
}
for(int i=0;i<k;i++){
cout<<q[i]<<" ";
}
return 0;
}
53C
和上面有关联的二分,所以在这里讲了
首先:|x|+|y|>n肯定不用考虑,因为一定不行
再者:|x|+|y|-n是奇数肯定不用考虑,因为多的路走了也回不去
最后:二分头尾差的长度,用长度去check能不能改变,条件依然是上面的判断
string s;
bool vis[maxn+5];
pair<ll,ll> node0[maxn+5],node1[maxn+5];
int n;
bool check(int len){
bool f=false;
for(int i=0;i<=m-len;i++){
ll rx=node1[i+len].first,ry=node1[i+len].second;//距离终点的距离 肯定的
ll lx=node0[i].first,ly=node0[i].second;//从源点出发走的距离 肯定的
ll cha=abs(rx-lx)+abs(ry-ly);
if(len>=cha&&((len-cha)%2==0)){
f=true;
break;
}
}
return f;
}
void solve(){
ll x,y,xy;
cin>>n;
cin>>s;
cin>>x>>y;
xy=abs(x)+abs(y);
if(xy>m)cout<<"-1\n";
else if((m-xy)%2)cout<<"-1\n";
else {
ll a1=0,a0=0,b1=0,b0=0;
mem(vis,0);
node0[0]={0,0};
for(int i=0;i<m;i++){
if(s[i]=='L')node0[i+1]={node0[i].first-1,node0[i].second};
if(s[i]=='R')node0[i+1]={node0[i].first+1,node0[i].second};
if(s[i]=='D')node0[i+1]={node0[i].first,node0[i].second-1};
if(s[i]=='U')node0[i+1]={node0[i].first,node0[i].second+1};
}
node1[m]={x,y};
for(int i=m-1;i>=0;i--){
if(s[i]=='L')node1[i]={node1[i+1].first+1,node1[i+1].second};
if(s[i]=='R')node1[i]={node1[i+1].first-1,node1[i+1].second};
if(s[i]=='D')node1[i]={node1[i+1].first,node1[i+1].second+1};
if(s[i]=='U')node1[i]={node1[i+1].first,node1[i+1].second-1};
}
if(node0[m].first==x&&node0[m].second==y){
cout<<"0\n";
return;
}
int l=1,r=m+1,mid,ans=1;
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
cout<<ans<<endl;
}
}
521E
从已知序列,找个序列,符合后一位是前一位的两倍,其中这个序列的和最大。
看题解的:题目标号没有用,只需要处理次数,map统计一下,再存入vector,进行排序。从1枚举到最多的那个数量,判断能不能往前找到当前值的1/2。
注意:这里有2e5最多2^18,所以最多找18次,于是枚举完全没问题,同时,为了避免可能的局部最优,这里的枚举要求枚举到最大的次数往前,而不是最小的次数往后。 另外:这里的数列求最值,不符合单调的二分性质,所以二分可能会有问题,三分能过(某队友三分过的)。
ll n;
map<ll,int>a;
void solve(){
scanf("%d",&m);
a.clear();
for(int i=0;i<m;i++){
scanf("%lld",&n);
a[n]++;
}
vector<ll>cnt;
for(auto it:a)cnt.push_back(it.second);
sort(cnt.begin(),cnt.end());
n=cnt[0];
int len=cnt.size()-1;
ll ans=0;
for(int i=1;i<=cnt[len];i++){
int pos=len;
ll cur=i;
ll res=cur;
while(cur%2==0&&pos>0){
cur/=2;
pos--;
if(cnt[pos]<cur)break;
res+=cur;
}
ans=max(ans,res);
}
cout<<ans<<endl;
}
难得的div3场,但是我被hack了。。。真惨