• 记第一场cf比赛(Codeforces915)


    比赛感想##

    本来21:05开始的比赛,结果记成21:30了。。。晚了25分钟才开始[捂脸]
    这次是Educational Round,所以还比较简单。

    前两道题一眼看去模拟+贪心,怕错仔细看了好几遍题,很快切掉
    第三题,dfs+贪心
    一开始想得有点简单,少了几种情况,写代码时才发现问题……
    悲伤地发现 写+调 这道题用了我很长时间…(这叫什么?基础不牢,地动山摇!)

    然后,居然只剩40分钟了……

    第四题,啊啊啊!
    图论,我的痛! 果断跳过
    第五题,额,不就是个线段树么?
    n<=10 (^9) ?不好不好,要动态开节点
    噼里啪啦噼里啪啦……
    提交。为什么超时了??? 我的常数真的这么大么??
    调一调…还是不行。。(……比赛结束后才发现是编译器的问题……)

    然后,居然只剩10分钟了……

    第六题,啊啊啊!怎么又是图论!
    图论,我的痛!果断跳过
    第七题,数论,有关gcd
    奇迹般地有了思路,可惜,时间不够没写完……

    接着,悲伤地发现比赛结束了。悲伤地发现我只做对了3道水题…
    嗯,水平还有很大提升空间啊,前方路还很长……


    题目+题解##

    Codeforces 915###

    A. Garden###

    Luba要给花园浇水,花园长度为k
    她有n个水桶,每个水桶一次浇的长度为(a_i) (不能多也不能少)
    她要选择1个水桶,使她浇得最快,且不会有地方被浇两次,不会浇到花园外面
    求她浇完的最短时间。
    (n,k,(a_i) (leq) 100)

    想法####

    在ai中找到可整除k的最大的数,用k除以这个数便是答案

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int main()
    {
        int n,k,i,x,ans=1000;
        scanf("%d%d",&n,&k);
        for(i=0;i<n;i++){
            scanf("%d",&x);
            if(k%x!=0) continue;
            ans=min(ans,k/x);                 
        }
        printf("%d
    ",ans);
        
        return 0;    
    }
    

    B. Browser###

    Luba在浏览器中打开了n个标签,从左到右标号1~n
    她只需[l,r]的标签,所以她要把其他的标签关上
    她的鼠标停在第pos个标签页上
    设她某时间鼠标位置为i,她可以有两种操作:
    1.关闭[1,i-1]或[i+1,n]中所有开着的标签
    2.将鼠标移到第i-1或第i+1个标签上 (前提:移到的那个标签必须是开着的)
    求她把除[l,r]外其他标签都关闭的最少操作数。
    (n (leq) 100)

    想法####

    分情况考虑+贪心

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int main()
    {
        int n,pos,l,r,s;
        scanf("%d%d%d%d",&n,&pos,&l,&r);
        
        if(l==1 && r==n) printf("0
    ");
        else if(l==1) printf("%d
    ",abs(r-pos)+1);
        else if(r==n) printf("%d
    ",abs(l-pos)+1);
        else {
            s=min(abs(l-pos),abs(r-pos));
            s+=r-l+2;
            printf("%d
    ",s); 
        }
        
        return 0;    
    }
    

    C. Permute Digits###

    给定a与b,求 将组成a的数字重新排列,组成的不大于b的最大整数
    注意,输出的整数与a的位数要一致,前导0要输出
    (a,b (leq) (10^{18})

    想法####

    若b的位数比a大,那么直接贪心,将组成a的数字从大到小输出
    否则,先把在b前补0使它与a的位数一致,接着从高位往低位考虑贪心,dfs回溯判断是否可行,若贪心到某一位发现 a的这一位<b的这一位,后面的位直接贪心

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    
    int a[10];
    char b[20],s[20];
    int m,ans[20];
    
    void Max(int cur){
        for(int i=9;i>=0;i--)
            for(int j=0;j<a[i];j++)
                ans[cur++]=i;
    }
    bool dfs(int cur){
        if(cur==m) return true;
        int i;
        if(a[i=b[cur]-'0']){
            ans[cur]=i;
            a[i]--;
            if(dfs(cur+1)) return true;
            a[i]++;
        }
        for(i=i-1;i>=0;i--)
            if(a[i]){
                ans[cur]=i;
                a[i]--;
                Max(cur+1);
                return true;         
            }
        return false;
    }
    
    int main()
    {
        int i,n;
        scanf("%s",s);
        n=strlen(s);
        for(i=0;i<n;i++) a[s[i]-'0']++;
        scanf("%s",b);
        m=strlen(b);
        
        if(m>n) Max(0);
        else{
            for(i=m-1;i>=0;i--) b[i+n-m]=b[i];
            for(i=0;i<n-m;i++) b[i]='0';
            m=n;
            dfs(0);   
        }
        for(i=0;i<n;i++) printf("%d",ans[i]);
        printf("
    ");
        
        return 0;    
    }
    

    D. Almost Acyclic Graph###

    给定一个n个点m条边的有向图
    问是否可以去掉一条边是图中不再有环
    ( 2 (leq) n (leq) 500 , 1 (leq) m (leq) min(n(n-1),100000) )

    想法####

    tarjan找scc,过程中记录某一个环
    若scc数目n,则yes
    否则,对于记录下来的那个环,试着把每条边删一遍,跑tarjan
    若删掉某条边后的scc数目
    n,则yes
    否则no (因为若删一条边满足条件的话,这条边一定在每个环中都出现)

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    
    const int N = 505;
    
    int dfn[N],map[N][N],low[N],vis[N],fa[N];
    int scc,n,cnt;
    int s[N],t;
    int cir[N],tot;
    
    void dfs(int u){
        dfn[u]=low[u]=++cnt;
        s[t++]=u; vis[u]=1;
        for(int v=1;v<=n;v++)
            if(map[u][v]){
                if(!dfn[v]){
                    fa[v]=u;
                    dfs(v);
                    low[u]=min(low[u],low[v]);
                }
                else if(vis[v]){ 
                    low[u]=min(low[u],dfn[v]);
                    if(!tot){
                        for(int i=u;i!=v;i=fa[i]) cir[tot++]=i;
                        cir[tot++]=v;        
                    }
                }
            }
        if(dfn[u]==low[u]){
            scc++;
            while(s[t-1]!=u) vis[s[--t]]=0;
            vis[s[--t]]=0;
        }
    }
    void tarjan() { for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); }
    
    int main()
    {
        int i,m,x,y,flag;
        scanf("%d%d",&n,&m);
        for(i=0;i<m;i++)
            scanf("%d%d",&x,&y),map[x][y]=1;
        
        tarjan();
        if(scc==n) puts("YES");
        else{
            flag=0;
            y=cir[tot-1];
            for(i=0;i<tot;i++){
                x=cir[i];
                memset(dfn,0,sizeof(dfn));
                map[x][y]=0; 
                cnt=scc=0; tarjan();
                if(scc==n) { flag=1; break; }
                map[x][y]=1;
                y=cir[i];           
            }
            if(flag) puts("YES");
            else puts("NO");
        }
    
        return 0;    
    }
    

    E. Physical Education Lessons###

    距学期结束还有n天,Alex需要在这n天上课
    但由于他的学校上课时间常变动(共q次),所以他想知道每次变动后他需要上多少天课
    有两种变动:
    1.[l,r]都不上课
    2.[l,r]都要上课
    假设一开始他要上n天课
    ( 1 (leq) n (leq) (10^9) , 1 (leq) q (leq) 300000 )

    想法####

    标准的线段树啊
    由于n最大1e9所以需要动态开节点

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    const int N = 300005;
    
    int n;
    
    struct node{
        node *ch[2];
        int sum,lazy;
    }pool[N*50],*root;
    int cnt;
    
    void pushdown(node *p,int l,int r){
        int mid=(l+r)>>1;
        if(!p->ch[0]){
            p->ch[0]=&pool[++cnt];
            p->ch[0]->sum=(mid-l+1);     
            p->ch[0]->lazy=-1;         
        } 
        if(!p->ch[1]){
            p->ch[1]=&pool[++cnt];
            p->ch[1]->sum=(r-mid);
            p->ch[1]->lazy=-1;              
        }
        if(p->lazy!=-1){
            p->ch[0]->sum=(mid-l+1)*p->lazy;
            p->ch[1]->sum=(r-mid)*p->lazy;
            p->ch[0]->lazy=p->ch[1]->lazy=p->lazy;
            p->lazy=-1;                
        }
        
    }
    void update(node *p) { p->sum=p->ch[0]->sum+p->ch[1]->sum; }
    void change(node *p,int l,int r,int L,int R,int k){
        if(p->sum==(r-l+1)*k) return;
        if(l==L && r==R){
            p->sum=k*(r-l+1);
            p->lazy=k;
            return;
        }
        pushdown(p,l,r);
        int mid=(l+r)>>1;
        if(mid>=R) change(p->ch[0],l,mid,L,R,k);
        else if(mid<L) change(p->ch[1],mid+1,r,L,R,k);
        else{
            change(p->ch[0],l,mid,L,mid,k);
            change(p->ch[1],mid+1,r,mid+1,R,k); 
        }
        update(p);
    }
    
    int main()
    {
        int q,i,k,l,r;
        scanf("%d%d",&n,&q);
        
        root=&pool[++cnt]; root->lazy=-1;
        root->sum=n;
        
        for(i=0;i<q;i++){
            scanf("%d%d%d",&l,&r,&k);
            change(root,1,n,l,r,k-1);
            printf("%d
    ",root->sum);
        }
        
        return 0;
    }
    

    F. Imbalance Value of a Tree###

    给定一棵n个节点的树,每个点都有权值ai
    函数I(x,y)的值为从x到y的唯一路径上点权最大-点权最小 (包括路径上的点x,y)
    求所有点对的I(x,y)之和
    (n (leq) (10^6)

    想法####

    若是暴力枚举每对点的话,就算求I为O(1)也会超时
    于是考虑函数I,实际上是求每对点间路径上的点权最小值之和 及 最大值之和
    先考虑最小值
    对于点权最小的那个点,显然所有经过它的路径上点权最小的都是它
    对于点权次小的点,所有经过它且不经过点权最小点的路径上,点权最小的都为它
    ……
    对于点权次大的点,它被计算当且仅当有一条边连接它与点权最大的点
    对于点权最大的点,它不会被计算

    由于是树,两两点间路径是唯一的
    于是就有这样一种做法:将每条边按照所连两点权值较小值 从大到小排序
    借助并查集,按排好的顺序合并边所连的两点,共n-1次
    每次合并,两个集合中各任取一点,它们之间的路径上点权最小值为 这条边所连两点权值较小值

    最大值同理,只不过是按边所连两点权值较大值 从小到大排序

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    const int N = 1000005;
    typedef long long ll;
    
    struct edge{
        int u,v;       
    }e[N];
    
    int val[N],fa[N],size[N];
    
    bool cmp1(edge x,edge y) { return min(val[x.u],val[x.v])>min(val[y.u],val[y.v]); }
    bool cmp2(edge x,edge y) { return max(val[x.u],val[x.v])<max(val[y.u],val[y.v]); }
    
    int Getfa(int x) { return fa[x]==x ? x : fa[x]=Getfa(fa[x]); }
    
    int n;
    
    int main()
    {
        int i,x,y,v;
        ll ans=0;
        scanf("%d",&n);
        for(i=1;i<=n;i++) scanf("%d",&val[i]);
        for(i=1;i<n;i++) scanf("%d%d",&e[i].u,&e[i].v);
        
        for(i=1;i<=n;i++) fa[i]=i,size[i]=1;
        sort(e+1,e+n,cmp1);
        for(i=1;i<n;i++){
            v=min(val[e[i].u],val[e[i].v]);
            x=Getfa(e[i].u); y=Getfa(e[i].v);
            if(size[x]<size[y]) swap(x,y);
            ans-=(ll)v*size[x]*size[y];
            fa[y]=x;
            size[x]+=size[y];    
        }
        
        for(i=1;i<=n;i++) fa[i]=i,size[i]=1;
        sort(e+1,e+n,cmp2);
        for(i=1;i<n;i++){
            v=max(val[e[i].u],val[e[i].v]);
            x=Getfa(e[i].u); y=Getfa(e[i].v);
            if(size[x]<size[y]) swap(x,y);
            ans+=(ll)v*size[x]*size[y];
            fa[y]=x;
            size[x]+=size[y];    
        }
        
        printf("%lld
    ",ans);
    
        return 0;    
    }
    

    G. Coprime Arrays###

    人们管满足 gcd((a_1),(a_2),…,(a_n)) =1的数组a叫Coprime Array
    给出n,k
    设对于i (in) [1,k] 满足每个元素都(in)[1,i] 的Coprime Array的个数为(b_i)
    (sumlimits_{i=1}^k) ( (b_i) ^ i)
    (n,k (leq) 2 ( imes) (10^6)

    想法一####

    定义对于一个数组a,lgcd=gcd((a_1),(a_2),…,(a_n))

    设 每个元素都(in)[1,i]的 满足lgcd=j 的数组个数为w[i][j]
    那么b[i]=(i^n)-(sumlimits_{j=2}^i) w[i][j]
    很容易发现,w[i][j]=b[$ frac {i} {j}$ ]
    接下来跟“余数求和”有那么一点像
    愉快地超时了……

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    #define P 1000000007
    
    using namespace std;
    
    typedef long long ll;
    const int N = 2000005;
    
    int PowerMod(int x,int b){
        int ret=1;    
        while(b){
            if(b&1) ret=((ll)ret*x)%P;
            x=((ll)x*x)%P;
            b>>=1;  
        }
        return ret;
    }
    
    int f[N];
    int n,k;
    
    int main()
    {
        int i,l,r,ans;
        scanf("%d%d",&n,&k);
        
        f[1]=1;
        ans=0;
        for(i=2;i<=k;i++){
            f[i]=PowerMod(i,n);
            for(l=2,r;l<=i;l=r+1){
                r=i/(i/l);
                if(r>i) r=i;
                f[i]=(f[i]-((ll)r-l+1)*(f[i/l]-P))%P; 
            }
            ans=(ans+(f[i]^i))%P;
        }
        printf("%d
    ",ans);
        
        return 0;    
    }
    

    想法二####

    想一想可以发现,b数组是递增的
    设f[i]=b[i]-b[i-1]
    f[i]统计的是至少有一个元素为i的Coprime Array个数
    和想法一的思路有一点点像
    设 每个元素都(in)[1,i]的 满足lgcd=j 且至少有一个元素为i 的数组个数为w[i][j]
    由于确定数组中一定有一个数是i,那么算出的lgcd值只能为i的约数
    很容易发现,w[i][j]=f[$frac {i} {j} $ ]
    那么f[i]=(i^n) - ((i-1)^n) - (sum){w[i][j] | j (in) [1,i] , i mod j=0 } (无比神奇的sum用法。。。)

    代码####

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    #define P 1000000007
    
    using namespace std;
    
    typedef long long ll;
    const int N = 2000005;
    
    ll PowerMod(int x,int b){
        ll ret=1;    
        while(b){
            if(b&1) ret=(ret*x)%P;
            x=((ll)x*x)%P;
            b>>=1;  
        }
        return ret;
    }
    
    ll f[N],p[N];
    int n,k;
    
    int main()
    {
        int i,j;
        ll ans,sum;
        scanf("%d%d",&n,&k);
        
        ans=sum=0;
        for(i=1;i<=k;i++){
            p[i]=PowerMod(i,n);
            f[i]=(f[i]+p[i]-p[i-1]+P)%P;
            sum=(sum+f[i])%P;
            ans=(ans+(sum^i))%P; 
            for(j=i*2;j<=k;j+=i)
                f[j]=(f[j]-f[i]+P)%P;
        }
        printf("%d
    ",ans);
        
        return 0;    
    }
    

    终于……

    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    java timer 指定某时间点执行
    java jdbc 同时操作查询删除操作
    java ResultSet获得总行数
    c# 根据枚举Value 获得名称
    c# 备份数据
    MySQL主从集群搭建
    单机安装MySQL多实例
    2019年度总结
    go goroutine id
    go语言中 json转换--nil
  • 原文地址:https://www.cnblogs.com/lindalee/p/8289118.html
Copyright © 2020-2023  润新知