Codeforces Round #189 (Div. 2) 解题报告

----------------

A. Magic Numbers

一个神奇的数字是由1、14、144连接而成的,判断一个数字是不是神奇数字。

----

①没有连续3个以上的4。

②首位不能为4。

数据范围太大,最好按字符读入。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

char c;
int n,num;
bool flag;

int main()
{
    flag=true;
    num=0;
    n=0;
    while (cin>>c)
    {
        if (n==0&&c!='1')
        {
            flag=false;
            break;
        }
        if (c!='1'&&c!='4')
        {
            flag=false;
            break;
        }
        if (c=='4') num++;
        else num=0;
        if (num>2)
        {
            flag=false;
            break;
        }
        n++;
    }
    if (flag) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
    return 0;
}
----------------

B. Ping-Pong (Easy Version)

两个数对(a,b)、(c,d),若c < a < d or c < b < 则数对(a,b)到数对(c,d)存在一条有向边。

① 1 a b 表示添加数对(a,b)

② 2 a b 询问是否有一条路径由第a个数对到第b个数对。

----

按要求建边,dfs判断是否连通即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int n;
bool a[111][111];
int x[111],y[111];
bool vis[111]={0};

bool dfs(int u,int v)
{
    vis[u]=true;
    if (u==v) return true;
    for (int i=0;i<n;i++)
        if (a[u][i]&&!vis[i])
            if (dfs(i,v)) return true;
    return false;
}

int main()
{
    int T;
    n=0;
    memset(a,0,sizeof(a));
    cin>>T;
    while (T--)
    {
        int t;
        cin>>t;
        if (t==1)
        {
            cin>>x[n]>>y[n];
            for (int i=0;i<n;i++)
            {
                if ((x[i]<x[n]&&x[n]<y[i])||(x[i]<y[n]&&y[n]<y[i])) a[n][i]=true;
                if ((x[n]<x[i]&&x[i]<y[n])||(x[n]<y[i]&&y[i]<y[n])) a[i][n]=true;
            }
            n++;
        }
        if (t==2)
        {
            int x,y;
            cin>>x>>y;
            memset(vis,0,sizeof(vis));
            if (dfs(x-1,y-1)) cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    }
    return 0;
}
----------------

C. Malek Dance Club

A组织的2^n个人与B组织的2^n配对跳舞。

配对规则为一个长度为n的二进制串x。

A组织的第i个人与B组织的第(i异或x)个人跳舞。

若有(a, b(c, d)配对,若 a < c and b > d. 则有一个配对的复杂度。

给出x,问两个组织配对跳舞的复杂度。结果mod1000000007 。

----

有图有真相。。。


图中左右两侧的点为A、B两组织的人员i的二进制编号,上方的x为异或用的二进制串。

在配对的两个人之间连一条线,则配对复杂度即为交点的个数。

 假设已经解决了二进制串x,现在要计算1x和0x的值。

此时已经计算出了f(x)的值。

由图观察可知:

①f(0x)=2*f(x)。

(设左右侧原来的编号为e,现在编号变为1e和0e,左0e和右0e配对,左1e和右1e配对,交点变为原来的2倍)

(而左侧0e和右侧1e之间并没有增加新交点,总交点数变为以前的2倍)

②f(1x)=2*f(x)+4^n。

(编号变为1e和0e交点变为以前的2倍,而左0e与右1e配对,左1e与右0e配对,左0e与右0e产生了2^n个新交点,同理左1e与右1e产生了2^n个新交点)

(共增加了4^n个交点)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>

using namespace std;

const int maxn=111;
const int MOD=1000000007;
long long f[maxn];
int n;
string x;
long long pw[maxn];

int main()
{
    pw[0]=1;
    for (int i=1;i<=100;i++)
    {
        pw[i]=pw[i-1]*4%MOD;
    }
    while (cin>>x)
    {
        n=x.length();
        f[n]=0;
        for (int i=n-1;i>=0;i--)
        {
            if (x[i]=='0') f[i]=2*f[i+1];
            if (x[i]=='1') f[i]=2*f[i+1]+pw[(n-i-1)];
            f[i]%=MOD;
        }
        cout<<f[0]<<endl;
    }
    return 0;
}
----------------

D. Psychos in a Line

n个人排成1排,每个人有一个SAN值。

每一回合,若一个人SAN值大于右边人的SAN值,则右边的人被杀。

所有杀害同时发生。剩下的人重新排列。

问多少回合后没有杀害发生。

----

题解暂无。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <cmath>

using namespace std;

const int maxn=111111;
const int INF=1e9+7;
int f[maxn];
int a[maxn];
int n;
stack<int>stk;

int main()
{
    int ans;
    while (cin>>n)
    {
        ans=0;
        while (!stk.empty()) stk.pop();
        memset(f,0,sizeof(f));
        for (int i=1;i<=n;i++) cin>>a[i];
        f[0]=INF;
        stk.push(0);
        for (int i=1;i<=n;i++)
        {
            f[i]=1;
            while (!stk.empty()&&a[i]>a[stk.top()])
            {
                f[i]=max(f[i],f[stk.top()]+1);
                stk.pop();
            }
            stk.push(i);
            if (f[i]<INF) ans=max(ans,f[i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}
----------------

E. Kalila and Dimna in the Logging Industry

Kalila和Dimna两个豺狼生活在一个巨大的丛林。有一天,他们为了赚钱加入了一个伐木厂。

伐木场的经理希望他们去树林里砍高度为a1,a2,a3…an的n课树。他们从一家商店里买了一台链锯。每次在编号为i的树上使用链锯,他们都能减少这棵树的一个单位高度。每次Kalila和Dimna使用链锯,他们需要重新对它充电。充电的成本依赖于已经被完全切断的树的编号id(一棵树高度为0时被完全切断)。如果被完全切断的树最大的编号为i(树一开始高度为ai),那么对链锯充电的成本为bi。如果没有树被完全切断,Kalila和Dimna不能对链锯充电。链锯在伐木开始之前已经充电。我们已知对于每个i<j,ai<aj并且bi>bj,而且bn=0、a1=1。Kalila和Dimna希望用最低的成本砍完所有的树木。

他们希望你来帮助他们,你会吗?

INPUT

第一行包含一个整数n (1 ≤ n ≤ 105),第二行包含n个整数a1,a2,a3…an (1 ≤ ai ≤ 109),第三行包含n个整数,b1,b2,b3….bn (0 ≤ bi ≤ 109)。

保证a1=1,bn=0,a1<a2<...<an并且b1>b2>…>bn

OUTPUT

输出只有一行,砍完所有树木的最低成本。

----

斜率DP

令f[i]为完全砍断第i棵树的最低成本。

如果最后一棵被砍断的树的编号为j,则f[i]=f[j]+a[i]*b[j]。

因此f[i]=min(f[j]+a[i]*b[j]) ( j<i )

简单dp时间复杂度为O(N*N)会超时,使用斜率优化可以变为O(N)的复杂度。

/*
//f[i]=min(f[j]+a[i]*b[j]) j<i
//-a[i]*b[j]+f[i]=f[j]
//令f[j]为y,b[j]为x,-a[i]为k,截距为dp[i]
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>

using namespace std;

typedef long long LL;

const int maxn=111111;

LL a[maxn],b[maxn],f[maxn];
int n;
int que[maxn*4];
int head,tail;
//y1-y2
LL fun(int k1,int k2){
    return f[k1]-f[k2];
}
//y1-y2>=k*(x1-x2)
bool cmp_head(int k1,int k2,int i){
    return fun(k1,k2)>=-a[i]*(b[k1]-b[k2]);
}
//y1-y2/x1-x2>=y2-yi/x2-xi
bool cmp_tail(int k1,int k2,int i){
    return double(fun(k1,k2))/double(b[k1]-b[k2])>=double(fun(k2,i))/double(b[k2]-b[i]);
}
//y-kx
LL dp_sol(int i,int j){return f[j]+a[i]*b[j];}
//y1-y2/x1-x2
double k_sol(int i,int j){return double(f[i]-f[j])/double(b[i]-b[j]);}

int main()
{
    while (cin>>n)
    {
        memset(f,0,sizeof(f));
        for (int i=0;i<n;i++) cin>>a[i];
        for (int i=0;i<n;i++) cin>>b[i];
        head=tail=0;
        que[tail++]=0;
        f[0]=0;
        for (int i=1;i<n;i++)
        {
            while (tail-head>1&&dp_sol(i,que[head])>=dp_sol(i,que[head+1])) head++;
            //while (tail-head>1&&cmp_head(que[head],que[head+1],i)) head++;
            f[i]=dp_sol(i,que[head]);
            while (tail-head>1&&k_sol(i,que[tail-1])>=k_sol(que[tail-1],que[tail-2])) tail--;
            //while (tail-head>1&&cmp_tail(que[tail-2],que[tail-1],i) ) tail--;
            que[tail++]=i;
        }
        cout<<f[n-1]<<endl;
    }
    return 0;
}


----------------

posted on 2013-07-13 20:17  电子幼体  阅读(184)  评论(0编辑  收藏  举报

导航