记第一场cf比赛

比赛感想

本来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\n",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\n");
    else if(l==1) printf("%d\n",abs(r-pos)+1);
    else if(r==n) printf("%d\n",abs(l-pos)+1);
    else {
        s=min(abs(l-pos),abs(r-pos));
        s+=r-l+2;
        printf("%d\n",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("\n");

    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\n",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\n",ans);

    return 0;
}

G. Coprime Arrays

人们管满足 gcd(\(a_1\),\(a_2\),…,\(a_n\)) 的数组a叫Coprime Array

给出n,k

设对于i \(\in\) [1,k] 满足每个元素都\(\in\)[1,i] 的Coprime Array的个数为\(b_i\)

求\(\sum\limits_{i=1}^k\) ( \(b_i\) ^ i)

(n,k \(\leq\) 2 \(\times\) \(10^6\))

想法一

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

设 每个元素都\(\in\)[1,i]的 满足lgcd=j 的数组个数为w[i][j]

那么b[i]=\(i^n\)-\(\sum\limits_{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\n",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\n",ans);

    return 0;
}


终于……

原文地址:https://www.cnblogs.com/lindalee/p/8289118.html

时间: 2024-11-10 19:44:17

记第一场cf比赛的相关文章

记第一场省选

其实,本来没什么好写的,我的确很水,如果正常发挥,应该是省赛边缘的水平,所以选不上也正常.可是第一天就出了一道题,发挥这么不正常,明显不应该,早上太糟心了,实在睡不着,起来总结了一下,感觉有以下几个原因.首先,太紧张.因为本来实力就不够,总担心这担心那,就更紧张了.第一道是模拟题,本来卡过那么多模拟题,这次又看到了很是心烦,但是还是要打,结果又犯了相同的错误,卡了两个多小时,实在是不应该……以后一定要注意细心,挑bug一定要全方面考虑,心态一定要稳,慌了就真慌了,模拟都是水题,相信自己,细心再细

HDU 4508 湫湫系列故事——减肥记I (2013腾讯编程马拉松初赛第一场)

http://acm.hdu.edu.cn/showproblem.php?pid=4508 题目大意: 给定一些数据. 每组数据以一个整数n开始,表示每天的食物清单有n种食物. 接下来n行,每行两个整数a和b,其中a表示这种食物可以带给湫湫的幸福值(数值越大,越幸福),b表示湫湫吃这种食物会吸收的卡路里量. 最后是一个整数m,表示湫湫一天吸收的卡路里不能超过m. 思路: 完全背包. 一开始以为是01背包. 敲了01后样例2不对啊!!! 然后改成完全就过了..就改循环体就好了.. #includ

我的第一场比赛——金马五校赛

这场比赛在东华大学举办,基于我上次普及组初赛没有过(本人过于蒟蒻),这算是我的第一场正式线下比赛. 我们学校信息组总共有九个人报名参加,我是其中第八名,但是在整场比赛中大约五五开. 以下是我的比赛心得: 这次比赛是在是发挥失常了.热身赛的时候排名还不错,三十几名,但是到了正式比赛就不行了.这场考试共十二道题目只做出2道. 第一题应该是很水的,但是我少考虑了一个条件,查了将近两个小时后才发现错误. 第二道和最后一道题目都可以用暴力做,但是也不知道为什么就是不让我过. 第六道题目是做的最快的题目,用

OCAC暑期比赛第一场 D题 足球比赛中的危险状态 题解

足球比赛中的危险状态原题链接:http://codeforces.com/problemset/problem/96/A[题目描述]灵灵非常喜欢足球.有一天,灵灵正在看一场足球比赛,在看比赛的同时灵灵在纸上写下了每一位运动员当前的位置.为了简化所有运动员的位置的描述,灵灵将其简化成了仅由字符 "0" 和 "1" 描述的一个字符串.每一个 "0" 表示主队的一个运动员,每一个 "1" 表示客队的一个运动员.如果当前的位置中存在连

2015 ACM多校训练第一场

在下面网址看效果更佳>_< http://mlz000.github.io/2015/08/07/2015-ACM%E5%A4%9A%E6%A0%A1%E8%AE%AD%E7%BB%83%E7%AC%AC%E4%B8%80%E5%9C%BA/ 题外话 这个暑假以前就决定要把这次多校的所有题全补了,中间断断续续,总算把第一场的题补全了,鄙视一下颓废的自己... hdu 5288(1001) OO's Sequence Solution 水题,定义两个数组L[i],R[i]示第i个数左侧和右侧最接

一场CF的台前幕后(上)——转

前奏 大约4月份的时候,业界毒瘤pyx噔噔噔跑过来说:“酷爱!我YY了一道题!准备当CF的C” 我当时就被吓傻了."Yet another Chinese round?" “区间取模,区间求和” 感觉这题还不错?不过pyx嫌水了…… 好办!当时我刚刚出完动态仙人掌不久,于是一拍脑袋说:把这个问题出到仙人掌上去! 当然被pyx鄙视了…… 后来一直就没啥动静,直到5月底的CTSC. 试机的时候pyx给我看了套他出的神题……里面有一道题……我不小心读成了下面这个样子: “给定n个m维的模2意

郁闷的第一场

我参加了在北京师范大学举办的第39届ACM竞赛.结果比我想象的还要悲惨,垫底了. 眼看旁边的气球一个个的增长,我们却…… 我学的是图论.在网络赛的时候就知道,图论很少出,一出了就可以是全场都不能AC的题目.这次我是看运气去的. 热身赛的时候,出现了一道图论的题目:给定N个数,任何P个连续的数之和是正数,任何Q个连续的数之和是负数.N的范围是0-100000. 我还挺高兴啊,是图论的差分约束.弄了半天都不对,最后是队友发现了错误.后来交上去是超时.后来邝斌告诉我们这题需要用拓扑排序来做. 第二天的

2014多校第一场J题 || HDU 4870 Rating(DP || 高斯消元)

题目链接 题意 :小女孩注册了两个比赛的帐号,初始分值都为0,每做一次比赛如果排名在前两百名,rating涨50,否则降100,告诉你她每次比赛在前两百名的概率p,如果她每次做题都用两个账号中分数低的那个去做,问她最终有一个账号达到1000分需要做的比赛的次数的期望值. 思路 :可以直接用公式推出来用DP做,也可以列出210个方程组用高斯消元去做. (1)DP1:离散化.因为50,100,1000都是50的倍数,所以就看作1,2,20.这样做起来比较方便. 定义dp[i]为从 i 分数到达i+1

微软2014编程之美初赛第一场——题目2 : 树

[来源] 题目2 : 树 [分析] 依据输入情况建立起树的模型.树的表示是一个表明父亲节点的数组.核心算法有两个: 计算某一节点的深度.用循环实现,一直向上找父亲节点,直到找到根节点.计算循环的次数即为深度. 计算某一节点的全部子节点.用递归实现. 本题在实现上节点的命名从0至N-1,与题目描写叙述不同. [代码] #include <iostream> #include <vector> using namespace std; vector<int> childre