[Codeforces #196] Tutorial

Link:

Codeforces #196 传送门

 

A:

枚举

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10;
int n,m,dat[MAXN],res=1<<30;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d",&dat[i]);
    sort(dat+1,dat+m+1);
    for(int i=1;i<=m-n+1;i++)
        res=min(res,dat[i+n-1]-dat[i]);
    printf("%d",res);
    return 0;
}
Problem A

 

B:

可以发现分母为$max(a*d,b*c)$,分子为$|a*d-b*c|$

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
int a,b,c,d,ta,tb,tc,td,resx,resy;
int GCD(int x,int y){return x%y==0?y:GCD(y,x%y);}

int main()
{
    scanf("%d%d%d%d",&a,&b,&c,&d);
    resx=abs(a*d-c*b),resy=max(a*d,c*b);
    int gcd=GCD(resx,resy);
    printf("%d/%d",resx/gcd,resy/gcd);
    return 0;
}
Problem B

 

C:

直接贪心,考虑将空格插入连续的序列中

如果可以不翻倍自然答案就是$m$,否则应当尽可能在前面翻倍来减少增加的分数

能推出翻倍$q$次后的分数为$(2^q-1)*2*k$,这样再加上剩余不翻倍的分数即可

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const ll MOD=1e9+9;
ll n,m,k,res;
ll quick_pow(ll a,ll b)
{
    ll ret=1;
    for(;b;b>>=1,a=a*a%MOD)
        if(b&1) ret=ret*a%MOD;
    return ret;
}

int main()
{
    scanf("%I64d%I64d%I64d",&n,&m,&k);
    if(!m||!n) return puts("0"),0;
    ll rst=n-m,num=n/k;
    if(rst>=num) res=m;
    else res=(quick_pow(2,num-rst)-1)*2*k%MOD+(m-(num-rst)*k%MOD);
    printf("%I64d",(res+MOD)%MOD);
    return 0;
}
Problem C

 

D:

官方题解:

将原树转化成有根树,对于一个点的最远关键点分成两类:

1、在该点的子树中,记最远距离为$MaxDown$

2、在树上的其它部分,记最远距离为$MaxUp$

对于1,明显可以$dfs$通过孩子节点来更新

对于2,又要分为两类来更新:

1、在其兄弟节点的子树中,即$MaxDown_{sibling}+2$

2、不在父亲节点的子树中,即$MaxUp_{father}+1$

其中注意用兄弟节点更新时要先记录最大/次大值来保证$O(1)$更新

 

不过感觉我的方法更简单啊……

先想想如何求一个点到树上任意点的最远距离

明显是用数的直径来解决的经典问题,可用反证法证明最远点一定是直径的两端

发现将任意点改为特定的关键点后该结论依然成立!

因此只要找到关键点中最远的点对$(a,b)$,对于每个点$v$判断$dist(a,v),dist(b,v)\le d$

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10;
int head[MAXN],tot;
struct edge{int nxt,to;}e[MAXN<<2];
int n,m,d,x,y,p[MAXN],rt1,rt2,d1[MAXN],d2[MAXN],res;

void add(int from,int to)
{e[++tot].nxt=head[from];e[tot].to=to;head[from]=tot;}
void dfs(int x,int anc,int *d)
{
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].to!=anc)
            d[e[i].to]=d[x]+1,dfs(e[i].to,x,d);
}

int main()
{
    scanf("%d%d%d",&n,&m,&d);
    for(int i=1;i<=m;i++) scanf("%d",&p[i]);
    for(int i=1;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    
    d1[1]=0;dfs(1,0,d1);
    for(int i=1;i<=m;i++) if(d1[p[i]]>d1[rt1]) rt1=p[i];
    d1[rt1]=0;dfs(rt1,0,d1);
    for(int i=1;i<=m;i++) if(d1[p[i]]>d1[rt2]) rt2=p[i];
    d2[rt2]=0;dfs(rt2,0,d2);
    
    for(int i=1;i<=n;i++)
        if(d1[i]<=d&&d2[i]<=d) res++;
    printf("%d",res);
    return 0;
}
Problem D

虽说第一种解法可能代码量稍大,但分类方法还是很实用的:

将无根树转化成有根树后按照是否在该点的子树中分类

其中兄弟节点对该点的贡献不要忘记统计!我一开始就漏掉了……

 

E:

发现最优解除了叶子节点和根外,其它节点必然都属于$a$

考虑一个点向当前树插入,其要么接在某个节点后,要么重开一棵树

由于$n$只有8,明显排序预处理后枚举各种组合就好了

 

其中有一些技巧需要注意:

1、采取从根到叶子的构造更加方便

由于树根确定其叶子节点就已经确定,后面接点时就不用再考虑叶子节点的贡献了

2、不要漏考虑最大点本身就是根的情况,这样答案会减少1

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
ll n,dat[10],dvs[10],res=1ll<<60;

void dfs(int k,int sum,int rt)
{
    if(k==n+1)
    {res=min(res,sum+ll(rt>1));return;}
    if(dvs[k]>1) sum++;
    
    dfs(k+1,sum+dvs[k],rt+1);//单独成树 
    for(int i=1;i<k;i++)//连在之前的节点后 
        if(dat[i]%dat[k]==0)
            dat[i]/=dat[k],dfs(k+1,sum,rt),dat[i]*=dat[k];
}

int main()
{
    scanf("%I64d",&n);
    for(int i=1;i<=n;i++) scanf("%I64d",&dat[i]);
    sort(dat+1,dat+n+1,greater<ll>());
    for(int i=1;i<=n;i++)
    {
        ll t=dat[i];
        for(int j=2;1ll*j*j<=dat[i];j++)
            while(t%j==0) t/=j,dvs[i]++;
        if(t>1) dvs[i]++; 
    }
    
    dfs(1,0,0);
    printf("%I64d",res);
    return 0;
}
Problem E

 

posted @ 2018-08-23 11:52  NewErA  阅读(184)  评论(0编辑  收藏  举报