Educational Codeforces Round 37

A - Water The Garden

/*
    给出一排格子,告诉你一些格子上有水,会往两边流动,问流满全部格子需要的时间
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
int main(){
    int T,n,m,a[210],d[210],ans;
    scanf("%d",&T);
    while(T--){
        ans=0;
        scanf("%d%d",&n,&m);
        memset(d,0x3f,sizeof(d));
        for(int i=1;i<=m;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++){
        	for(int j=1;j<=m;j++){
        		d[i]=min(d[i],abs(i-a[j])+1);
        	}ans=max(ans,d[i]);
        }    
        printf("%d\n",ans);
    }return 0;
}

B - Tea Queue

/*
    告诉你n个人来打水的时间和等待到的最迟时间,来的时间相同的话编号小的排前面
    问每个人取到水的时间,如果没取到水,则输出0
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=5010; 
int T,n,l[N],r[N],ans[N];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]);
        int nowt=0;
		for(int i=1;i<=n;i++){
			if(nowt>r[i]){ans[i]=0;continue;}
        	if(l[i]>=nowt)ans[i]=l[i],nowt=l[i]+1;
			else ans[i]=nowt,nowt++; 
        }for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
    }return 0;
}

C - Swap Adjacent Elements

/*
    告诉你一个a序列,和一个s序列,s序列为1表示这个位置的a序列可以和下一个互换,
    问能否通过随意交换可互换元素将a序列排序
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[200010];
char s[200010];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    scanf("%s",s+1);s[n]='0';
    for(int i=1;i<n;i++){
        if(s[i]=='1'){
            for(int j=i;j<=n;j++){
                if(s[j]=='0'){
                    sort(a+i,a+j+1);
                    i=j;
                    break;
                }
            }
        }
    }
    int flag=1;
    for(int i=1;i<n;i++){
        if(a[i]>a[i+1]){puts("NO");flag=0;break;}
    }if(flag)puts("YES");
    return 0;
}

D - Tanks

/*
    题目大意:给出N个容量无限的桶,每个桶中开始都有一些水,给出一个大小为K的勺子,
    问你能否通过N+5次以内的勺水操作(从一个桶到另一个桶),使得最后能有一个桶中的
    水为V
    题解:我们将所有的桶中的水量对K取模,如果这些值能够取一些相加取模后得到V%K,
    说明答案可行,我们可以用01背包解决这个问题,对于路径输出,我们在做dp的时候记录路径,
    表示这个位置是答案的组成部分,那么根据是否是答案的组成部分,我们将桶分为两部分,
    对于处于同个部分的桶,我们把水全都倒进一个桶中,
    那么最后只要在这两个桶相互倒就可以得到答案
*/
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN=5010;
int N,K,V,S,a[MAXN];
bool dp[MAXN][MAXN],pre[MAXN][MAXN];
vector<int> vc[2];
void Solve(){
    for(int i=1;i<=N;i++)S+=a[i];
    if(S<V){puts("NO");return;}
    dp[0][0]=1;
    for(int i=1;i<=N;i++){
        int x=a[i]%K;
        for(int j=0;j<K;j++)if(dp[i-1][j]){
            dp[i][j]=1; pre[i][j]=0;
            dp[i][(j+x)%K]=1; pre[i][(j+x)%K]=1;
        }
    }if(!dp[N][V%K]){puts("NO");return;}
    int v=V%K;
    for(int i=N;i>0;i--){
        int x=a[i]%K;
        if(pre[i][v]){
            vc[1].push_back(i);
            v=(v-x+K)%K;
        }else vc[0].push_back(i);
    }
    puts("YES");
    sort(vc[0].begin(),vc[0].end());
    sort(vc[1].begin(),vc[1].end());
    for(int k=0;k<2;k++)if(vc[k].size()){
        for(int i=1;i<vc[k].size();i++)printf("%d %d %d\n",1000000000,vc[k][i],vc[k][0]);
    }
    int x0,x1,s=0;
    for(int i=0;i<vc[1].size();i++)s+=a[vc[1][i]];
    if(!vc[0].size())x1=vc[1][0],x0=vc[1][1];
    else if(!vc[1].size())x1=vc[0][1],x0=vc[0][0];
    else x1=vc[1][0],x0=vc[0][0];
    if(s<V)printf("%d %d %d\n",(V-s)/K,x0,x1);
    if(s>V)printf("%d %d %d\n",(s-V)/K,x1,x0);
}
int main(){
    scanf("%d%d%d",&N,&K,&V);
    for(int i=1;i<=N;i++)scanf("%d",&a[i]);
    Solve(); 
    return 0;
}

E - Connected Components?

/*
    题目大意:给出一张图,求补图的连通块和其大小
    题解:搜索的理论复杂度O(n^2),显然不行,
    bfs的时候对于已经归入其余连通块的点用并查集进行段无效信息处理,减少搜索树的分支,
    显然经过这样的处理搜索分支的数量下降得非常快,就能顺利解决此题了
*/
#include <cstdio>
#include <algorithm>
#include <queue> 
using namespace std;
const int N=200010;
int f[N],n,m,vis[N],ans,cnt[N];
vector<int> v[N];
int sf(int x){return f[x]==x?x:f[x]=sf(f[x]);}
void bfs(int st){
    queue<int> q;
    q.push(st);
    cnt[++ans]=1;
    while(q.size()){
        int x=q.front();q.pop();f[x]=sf(x+1);
        for(int i=0;i<v[x].size();i++)vis[v[x][i]]=x;
        for(int i=sf(1);i<=n;i=sf(i+1))if(vis[i]!=x){
            cnt[ans]++;
            f[i]=sf(i+1);
            q.push(i);
        }
    }
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        ans=0;
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        for(int i=1;i<=n+1;i++)f[i]=i;
        for(int i=1;i<=n;i=sf(i+1))bfs(i);
        printf("%d\n",ans);
        sort(cnt+1,cnt+ans+1);
        for(int i=1;i<=ans;i++)printf("%d%c",cnt[i],i==ans?'\n':' ');
    }return 0;
}

F - SUM and REPLACE

/*
    题目大意:给出一个序列,要求维护区间求和,和区间修改操作,
    修改操作是将区间中每个数变为其约数个数
    题解:我们计算一下约数个数发现,D(1)=1,D(2)=2,如果一个区间全是这样的数字的话
    我们就不需要对这个区间进行修改,所以我们只要暴力改数值,当发现一个区间的最大值是1或2时
    我们就停止往下修改
*/
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1000010,M=2000010;
int p[N];
namespace Segment_Tree{
    int tot;
    struct node{int l,r,a,b;LL mx,val;}T[M];
    void build(int,int);
    void Initialize(int n){
        tot=0;
        build(1,n);
    } 
    void up(int x){
        T[x].val=T[T[x].l].val+T[T[x].r].val;
        T[x].mx=max(T[T[x].l].mx,T[T[x].r].mx);
    }
    void build(int l,int r){
        int x=++tot;
        T[x].a=l;T[x].b=r;T[x].l=T[x].r=T[x].val=T[x].mx=0;
        if(l==r){scanf("%lld",&T[x].val);T[x].mx=T[x].val;return;}
        int mid=(l+r)>>1;
        T[x].l=tot+1;build(l,mid);
        T[x].r=tot+1;build(mid+1,r);
        up(x);
    }
    void change(int x,int a,int b){
        if(T[x].mx==1||T[x].mx==2)return;
        if(T[x].a==T[x].b){T[x].mx=T[x].val=p[T[x].val];return;}
        int mid=(T[x].a+T[x].b)>>1;
        if(mid>=a&&T[x].l)change(T[x].l,a,b);
        if(mid<b&&T[x].r)change(T[x].r,a,b);
        up(x);
    }
    LL query(int x,int a,int b){
        if(T[x].a>=a&&T[x].b<=b)return T[x].val;
        int mid=(T[x].a+T[x].b)>>1; LL res=0;
        if(mid>=a&&T[x].l)res+=query(T[x].l,a,b);
        if(mid<b&&T[x].r)res+=query(T[x].r,a,b); 
        return res;
    } 
}
int n,m,t,l,r;
int main(){
    for(int i=1;i<=1000000;i++){
        for(int j=i;j<=1000000;j+=i)p[j]++;
    }
    scanf("%d%d",&n,&m);
    Segment_Tree::Initialize(n);
    while(m--){
        scanf("%d%d%d",&t,&l,&r);
        if(t==1)Segment_Tree::change(1,l,r);
        else printf("%lld\n",Segment_Tree::query(1,l,r));
    }return 0;
}

G - List Of Integers

/*
    题目大意:求出比x大的和p互质的第k大的数
    题解:我们二分答案,将题目转化为判定区间内和一个数互质的数是否大于k
    求区间内和一个数互质的数的个数我们可以用容斥来解决,将这个数的约数筛选出来,
    然后容斥区间内约数个数出现的次数即可
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=10000010;
typedef long long LL;
#define pw(x) (1LL<<x)
int T;
LL x,y,k,p;
LL cal(LL x,vector<LL> &p){
    LL res=0;
    int n=p.size();
    for(int i=0;i<pw(n);i++){
        LL t=1,f=1;
        for(int j=0;j<n;j++)if(i>>j&1)t*=p[j],f*=-1;
        res+=f*(x/t);
    }return res;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld",&x,&p,&k);
        vector<LL> f;
        for(LL i=2;i*i<=p;i++)if(p%i==0){
            f.push_back(i);
            while(p%i==0)p/=i;
        }
        if(p>1)f.push_back(p);
        k+=cal(x,f);
        LL l=1LL,r=1000000000000LL,ans;
        while(l<=r){
            LL mid=(l+r)/2;
            if(cal(mid,f)>=k)ans=mid,r=mid-1;
            else l=mid+1;
        }printf("%lld\n",ans);
    }return 0;
}

 

posted @ 2018-02-07 22:15 forever97 阅读(...) 评论(...) 编辑 收藏