Chieh's Blog

Because Of Coding

R290 DIV2 C. Fox And Names

飞机票:http://codeforces.com/problemset/problem/510/C

题意:就是给你n个字符串,按字典序从上到下递增,判断字典序是否正确,正确输出任意一个可行的方案,不行则impossible

分析:刚开始看对题,但是自己想法错误,大晚上想法不行啊后来还是想回来了,我的想法是分治法。

假设n个字符串,第i个到第j个(i<j)的第一个字符都是一样的,则它们还得比较第二个字符。。。。第k个字符,直到不一样为止。

当不一样时我们就可以加边了,假设不一样的字符是第k个,则字符串i的第k个字符<字符串j的第k个字符,加单向边,一直到最后,然后用floyd判断有无环,有环则impossible,没环则输出可行方案可以方案怎么输出,我是枚举的,因为数量不大,假设当前的字符是i,则判断有没有j可以到i的,如果j输出过了,则跳过,如果没有,则可以证明i是当前最小的,把i输出来,然后标记i就行了。。。这里还有个地方,就是字符串长度的问题,假设到比较第k个字符串了,来个标记,如果从i到p是空的,p+1是不空的,则改变标记,这样后面如果有空的,就是impossible,直接返回~

PS:大晚上明明返回标记了,后面忘了判断输出了,fst了改一下就AC了

/*
Author:Chieh
Because of You
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <stack>
#define LL long long
using namespace std;
map<char,int> M;
map<int,char> M1;
const int maxn=30;
int ai[maxn][maxn];
char ch[123][123];
int n;
int len;
void init()
{
    len=0;
    for(int i=1; i<=n; i++)
    {
        scanf("%s",ch[i]+1);
        int q=strlen(ch[i]+1);
        len=max(len,q);
    }
}
bool flag;
int st;
void DFS(int now,int l,int r)
{
    if(!flag)return ;
    char before='A';
    int ks=0;
    int os=-1;
    for(int i=l; i<=r; i++)
    {
        int q=strlen(ch[i]+1);
        char p=ch[i][now];
        if(now>q&&os==-1)continue;
        os=1;
        if(now>q)
        {
            flag=0;
            return;
        }
        if(before=='A')
        {
            ks=l;
            before=ch[i][now];
        }
        else
        {
            if(before==p)
            {
                continue;
            }
            else
            {
                if(M[before]==0)
                {
                    M[before]=st;
                    M1[st]=before;
                    st++;
                }
                if(M[p]==0)
                {
                    M[p]=st;
                    M1[st]=p;
                    st++;
                }
                int u=M[before];
                int v=M[p];
                before=p;
                ai[u][v]=1;
                if(i-1==ks)
                {
                    ks=i;
                    continue;
                }
                else
                {
                    DFS(now+1,ks,i-1);
                }
                ks=i;
            }
        }
    }

    if(r==ks)return;
    else if(ks==0)return;
    DFS(now+1,ks,r);
}
bool vis[maxn];
void play()
{
    memset(ai,0,sizeof(ai));
    memset(vis,0,sizeof(vis));
    flag=1;
    M.clear();
    M1.clear();
    st=1;
    DFS(1,1,n);
    if(!flag)
    {
        printf("Impossible\n");
        return;
    }
    for(int k=1; k<=29; k++)
    {
        for(int i=1; i<=29; i++)
        {
            for(int j=1; j<=29; j++)
            {
                if(ai[i][k]&&ai[k][j])
                {
                    ai[i][j]=1;
                }

            }
        }
    }

    for(int i=1; i<=29; i++)
    {
        for(int j=1; j<=29; j++)
        {
            if(ai[i][j]&&ai[j][i])
            {
                printf("Impossible\n");
                return;
            }
        }
    }
    for(char  i='a'; i<='z'; i++)
    {
        if(M[i]==0)printf("%c",i);
    }
    while(1)
    {
        bool ok=0;
        for(int i=1; i<=29; i++)
        {
            if(vis[i])continue;
            if(M1[i]!=0)
            {
                bool flag=1;
                for(int j=1; j<=29; j++)
                {
                    if(vis[j])continue;
                    if(M1[j]==0)continue;
                    if(ai[j][i])
                    {
                        flag=0;
                        break;
                    }
                }
                if(flag)
                {
                    printf("%c",M1[i]);
                    vis[i]=1;
                    ok=1;
                }
            }
        }
        if(!ok)break;
    }
    printf("\n");

}
int main()
{

    while(scanf("%d",&n)!=EOF)
    {
        init();
        play();
    }
//    cout << "Hello world!" << endl;
    return 0;
}

 

UVALive - 4254 Processor

飞机票: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=21663

题意:就是给你n个需要处理的事情,第i个必须在li~ri完成,且数量是wi,完成时最大的速度最小是多少;

分析:求最大的速度最小我们可以使用二分来选择速度,但是怎么判断呢?果然还是太菜;其实可以根据时间来判断;具体怎么判断,时间递增,假设当前是j,则把所有左端点小于等于j的都压入到队列中,且按右端点排序(why?因为右端点越大我们可以在后面处理,所以这里贪心的想法),然后设当前还可以处理now个任务,当前碰到的是i,且任务数是w,我们可以判断下now和i的大小,把now和i都减去小的那个,如果now==0了,则退出了,因为不能再处理任务了,如果w不等于0则还没处理完,在压入到队列里。。。没一次j处理玩,可以判断下,如果最小的那个没处理完的r<=j则表明速度太小,返回false(why??)因为下面大于j的时候已经不能处理它了,如果队列空了,且处理完了那么返回true,你懂得。。。。起先没有想到,,,太渣了。还是看别人的才反映过来。。。啪啪啪,就可以AC了~

/*
Author:Chieh
Because Of You
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#define LL long long
using namespace std;
const int maxn=12345;
struct he
{
    int l,r,w;
    bool operator <(const he &a)const
    {
        return r > a.r;
    }
} JLJ[maxn];
priority_queue<he> q;
bool cmp(he a,he b)
{
    return a.l<b.l;
}
int n;
void init()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d%d",&JLJ[i].l,&JLJ[i].r,&JLJ[i].w);
    }
}
bool check(int x)
{
    int i = 1, j = 0;
    while (!q.empty())
        q.pop();
    while (1)
    {
        while (i <= n && JLJ[i].l <= j)
            q.push(JLJ[i++]);
        int now = x;
        while (now != 0 && !q.empty())
        {
            he temp = q.top();
            q.pop();
            int m = min(now,temp.w);
            now -= m;
            temp.w -= m;
            if (temp.w != 0)
                q.push(temp);
        }
        j++;
        if (!q.empty() && q.top().r <= j)
            return false;
        if (q.empty() && i ==n+1)
            return true;
    }
}
void play()
{
    sort(JLJ+1,JLJ+1+n,cmp);
    int l=1;
    int r=1000000000;
    int MIN=r;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid))
        {

            r=mid-1;
            MIN=min(mid,MIN);
        }
        else
        {
            l=mid+1;
        }
    }
    printf("%d\n",MIN);
}
int T;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }
    // cout << "Hello world!" << endl;
    return 0;
}

 

UVALive - 3902 Network

飞机票: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16451

题意:就是给你一棵树,给你一个服务器,求在其余非叶子节点上放服务器,使任意的叶子节点到最近服务器的距离不超过k

分析:可以根据树形的性质来进行解决

具体步骤:用pair来存数据,如果first是1就是说有叶子节点没有服务器,如果first是2就说有服务器在;所以当我们搜到叶子节点的时候,返回(1,1),当搜到父子节点,我们对返回的子节点进行贪心选择,如果是1就选择距离最大的没有服务器的叶子节点,如果是2就选择最近的服务器,如果1+2的距离(1,为叶子节点最远距离,2为服务器最近距离)<=k那么就是可以访问到,则返回2,且服务器的距离要加1;如果>k那么就是说访问不到,那么我们要判断,如果1==k则必须架服务器了,不加则不满足条件,如果<k则返回叶子节点距离加1.一直递归到s节点,且s节点本身的2为0。。。ok复杂度为O(n-1)即边的数量;

啪啪啪。就可以AC了

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#define LL long long
using namespace std;
const int maxn=1234;
vector<int> V[maxn];
int n,T,s,k;
void init()
{
    scanf("%d%d%d",&n,&s,&k);
    for(int i=1; i<=n; i++)V[i].clear();
    int u,v;
    for(int i=1; i<n; i++)
    {
        scanf("%d%d",&u,&v);
        V[u].push_back(v);
        V[v].push_back(u);
    }
}
int MIN;
pair<int,int> DFS(int now,int fa)
{
    int a1=-1;
    int a2=-1;
    if(now==s)a2=0;
    for(int i=0; i<V[now].size(); i++)
    {
        int v=V[now][i];
        if(v==fa)continue;
        pair<int,int> p= DFS(v,now);
        if(p.first==1)
        {
            a1=max(a1,p.second);
        }
        else
        {
            if(a2==-1)a2=p.second;
            else
                a2=min(a2,p.second);
        }
    }
    if(a1==-1&&a2==-1)
    {
        return make_pair(1,1);
    }
    if(a1!=-1&&a2!=-1)
    {
        if(a1+a2<=k)
        {
            return make_pair(2,a2+1);
        }
        else
        {
            if(a1==k)
            {
                MIN++;
                return make_pair(2,1);
            }
            return make_pair(1,a1+1);
        }
    }
    if(a1==-1)
    {
        return make_pair(2,a2+1);
    }
    if(a1==k)
    {
        MIN++;
        return make_pair(2,1);
    }
    return make_pair(1,a1+1);
}
void play()
{
    MIN=0;
    DFS(s,0);
    printf("%d\n",MIN);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }
    return 0;
}

UVA - 10795 A Different Task

飞机票:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=34699

题意:就是给你初始的汉诺塔大小升序,和目标的汉诺塔大小升序,求最小的移动步数;

分析:刚开始没有想法,后来想到了万法归宗,就是已知初始状态变回原始状态也是可行的就是说假如A,B,C三个分别有编号1,2,3的盘子,我们可以变回A,B,C只有A上有3个盘子的状态好复杂,所以我是用了反推的方法,先设A,B,C有3个盘子,然后推出A,B,C分别有1个盘子的步数为几步。。所以加个判断,加入当前盘子是A,当前要移动的最大盘是k,要移动到B,则上面的k-1个盘都要移动到C才行,就是2^(k-1)步,这里不用减1,因为k要移一步。这样先把初始的都先归到一个盘子一共要几步,然后根据当前的盘要移动到哪进行计算步数就ok了,

解释下吧:

对于第一个样例,我们可以把第3个盘提出来,则1,2个盘回到C上去,这样一共要3步+第三个盘要1步,就是4步,然后1,2都在C上,当要把2移到B上时,我们得把2上面的移到A上,则要1步+第二个盘要1步,就是2步,最后再加上第一个盘移一次就是答案了,7;

啪啪啪~就可以AC了

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <stack>
#define LL long long
using namespace std;
const int maxn=67;
struct he
{
    int s,e;
} JLJ[maxn];
int n;
void init()
{
    for(int i=1; i<=n; i++)scanf("%d",&JLJ[i].s);
    for(int i=1; i<=n; i++)scanf("%d",&JLJ[i].e);
}
void play()
{
    int st=-1;
    for(int i=n; i>=1; i--)
    {
        if(JLJ[i].s!=JLJ[i].e)
        {
            st=i;
            break;
        }
    }
    if(st==-1)
    {
        printf("0\n");
        return;
    }
    LL sum=0;
    int now=6-JLJ[st].s-JLJ[st].e;
    for(int i=st-1; i>=1; i--)
    {
        if(JLJ[i].s==now)
            continue;
        else
        {

            sum+=((1LL)<<(i-1));
            now=6-JLJ[i].s-now;
        }
    }
    sum++;
    now=6-JLJ[st].s-JLJ[st].e;
    for(int i=st-1; i>=1; i--)
    {
        if(JLJ[i].e==now)continue;
        else
        {
            sum+=((1LL)<<(i-1));
            now=6-JLJ[i].e-now;
        }
    }
    printf("%lld\n",sum);
}
int main()
{
    int Ca=1;
    while(scanf("%d",&n)!=EOF)
    {   if(n==0)break;
        init();
        printf("Case %d: ",Ca++);
        play();
    }
    return 0;
}

UVA - 11464 Even Parity

飞机票: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=24665

题意:就是说给你个矩阵,你把0变为1,使矩阵中各个点的上下左右相加为偶数(如果上下左右存在的话),求改变最少的0的数量

分析:如果枚举每个点的话肯定就TLE了,但是我们可以只枚举第一行就行。。。这样就可以推出下面所有的行,因为下面的行可以根据当前行的上左右推出来,复杂度为2^n*(n^2),可以接受~,啪啪啪,AC啦

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
#include <stack>
#define LL long long
using namespace std;
const int maxn=20;
int T,n;
int ai[maxn][maxn];
int bi[maxn][maxn];
void init()
{
    scanf("%d",&n);
    memset(ai,0,sizeof(ai));
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            scanf("%d",&ai[i][j]);
        }
    }
}
int MIN;
void DFS(int depth,int sum)
{

    if(depth>n)
    {
        int now=sum;
        memset(bi,0,sizeof(bi));
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                bi[i][j]=ai[i][j];
            }
        }
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                int tm=bi[i-1][j]+bi[i][j-1]+bi[i][j+1];
                if(tm%2==0)
                {
                    if(bi[i+1][j]==1)return;
                    else continue;
                }
                else
                {
                    if(bi[i+1][j]==0&&i+1<=n)
                    {
                        bi[i+1][j]=1;
                        now++;
                    }
                    else if(bi[i+1][j]==1)continue;
                    else
                    {
                        return;
                    }
                }
            }
        }
        MIN=min(now,MIN);
        return;

    }
    DFS(depth+1,sum);
    if(ai[1][depth]==0)
    {
        ai[1][depth]=1;
        DFS(depth+1,sum+1);
        ai[1][depth]=0;
    }

}
void play(int Case)
{
    MIN=1000000000;
    DFS(1,0);
    if(MIN==1000000000)MIN=-1;
    printf("Case %d: %d\n",Case,MIN);
}
int Case;
int main()
{   Case=1;
    scanf("%d",&T);
    while(T--)
    {
        init();
        play(Case);
        Case++;
    }
    //cout << "Hello world!" << endl;
    return 0;
}

 

ZOJ Problem Set - 3468 Dice War

飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4254

分析:其实这个题难懂的是题意啊~~~后来百度下骰子游戏菜知道这个题的意思。。先来看下骰子游戏的规则

规则略微有点复杂,刚开始可能不太明白,我详细解释下:

你的终极目标是占领所有的地块,你的颜色是紫色。
占领地块的方法就是用你的地块攻击相邻的地块,胜负由这两个地块上的骰子掷出点数决定。
1)如果你赢了,那么原来地块上的骰子变成一个,新占领地块上的骰子数 = 原来地块骰子数 - 1 ;
2)如果平了或者输了,那么你的地块上的骰子也会变成一个。
只要你的地块上的骰子数大于一个,就可以攻击别人。
在攻击结束时,点击 "End Turn" 可以补充兵力,补充的总数是你此时连在一起的地块的总数,分配则是随机的。每个地块上最多有 8 颗骰子。

提示策略:刚开始的时候尽量把自己的地都连到一起,中期的时候怎样布阵对于进攻和防守都很重要,后期基本都是八颗八颗的硬碰硬了 :P

看了应该就秒懂了吧。反正题目就是说给你攻击者的骰子数和防御者的骰子数。然后求攻击者赢得概率(就是说攻击者的点数严格大于防御者的点数)。。。这里要注意:如果攻击者的骰子数是1,那么就不能攻击,意思就是说赢得概率是0。。。然后你就可以根据概率来搞了。。。对于数学概率的渣渣,,,只有这么一个想法就是说把攻击者的点数枚举。。。怎么枚举法??

已知n,那么攻击者的点数就是在n~6*n之间。。。然后枚举每个点数所占的概率。。。就是 k的可能方法有几种/6^n就是k的概率、、然后你要赢:那么就是m~6*m之间小于k的概率 那么就是(m~k-1)之间所有可能的方法/6^m的概率、、、然后从将所有的相加就是答案。。。这里计数方法,我先是DFS。。。其实也不慢,但是也不快。。。后来想到了DP。。。就是按骰子数来DP,然后当前骰子有6中可能1~6,然后看前面的是否出现过k,出现过,那么当前(k+(1~6))就要累加了。。。这样复杂度就直接变为(6*n)^2

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#define LL long long
using namespace std;
int ai[2][9][49];
int bi[2];
int p;
void play()
{
    if(bi[0]<=1)
    {
        printf("0\n");
        return;
    }
    for(int z=0; z<=1; z++)
    {
        int e=6*bi[z];
        memset(ai[z][0],0,sizeof(ai[z][0]));
        ai[z][0][0]=1;
        for(int j=1; j<=bi[z]; j++)
        {
            memset(ai[z][j],0,sizeof(ai[z][j]));
            for(int i=1; i<=6; i++)
            {
                for(int k=0; k<=e; k++)
                {
                    if(ai[z][j-1][k]>0)
                    {
                        ai[z][j][k+i]+=ai[z][j-1][k];
                    }
                }
            }
        }
    }
    int fm=pow(6.0,bi[0]);
    int fm1=pow(6.0,bi[1]);
    for(int i=1; i<=6*max(bi[0],bi[1]); i++)
    {
        ai[1][bi[1]][i]=ai[1][bi[1]][i]+ai[1][bi[1]][i-1];
    }
    double over=0;
    for(int i=1; i<=6*bi[0]; i++)
    {
        double pop=((double)(ai[0][bi[0]][i]))/fm;
        double pop1=((double)(ai[1][bi[1]][i-1]))/fm1;
        over=over+(pop*pop1);
    }
    printf("%.8lf\n",over);
}
int main()
{
    while(scanf("%d%d",&bi[0],&bi[1])!=EOF)
    {
        play();
    }
    return 0;
}

 

ZOJ Problem Set - 1990 Subway Tree Systems

飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=990

由于昨天有事。。。所以没怎么细想。。。然后一直以为只要深度相同。。。然后子节点个数相同就可以了。。。然后就一直Wa。。。后来自己把自己的想法推翻了。。。直接看下图:

然后这里的话。。。如果按我起先的想法肯定是wa的因为1层都有2个子节点。二层有3个节点和2个节点。三层有2,0,0,3,0个节点

但是这两棵树明显不同、、debug爽啊。。。接着我的想法就是。。。那把每个节点的深度算出来和他的子树节点的深度都算出来。。。然后排序比较。。。这个想法明显是正确。。但是速度不快啊~~~囧。然后百度了一下。。。有一种方法是跟括号匹配差不多。。。就是说经过父节点(,回到了父节点就),然后括号里面的可以交换位置。。。交换出最小字典序。。。然后比较是否相同,还有一种方法:好像说是只要把深度算出来然后计算子树节点数排序比较就可以了。。。但是为什么呢。。感觉也不怎么对。。。画个图。。。其实这个想法是错误的

这个的序列是0010101001011100101011和0010101010101100010111。。。然而这个是diff。但是前面的跑出来的是same。。所以错误的。。。估计测试数据没有这个数据。我还是不传输错误的代码了。。。就用那个速度慢的吧

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <stack>
#define LL long long
using namespace std;
const int maxn=3456;
char ch[2][maxn];
struct he
{
    int dep;
    vector<int> V;
} JLJ[2][1567];
void init()
{
    scanf("%s%s",ch[0]+1,ch[1]+1);
}
int idx,st, p;
int len;
void DFS(int now,int dep)
{
    JLJ[p][now].dep=dep;
    while(idx<=len&&ch[p][idx]=='0')
    {
        idx++;
        st++;
        int q=st;
        DFS(q,dep+1);
        for(int j=0; j<JLJ[p][q].V.size(); j++)
        {
            JLJ[p][now].V.push_back(JLJ[p][q].V[j]+1);
        }
    }

    JLJ[p][now].V.push_back(0);
    sort(JLJ[p][now].V.begin(),JLJ[p][now].V.end());
    idx++;
    return;

}
bool cmp(he a,he b)
{
    if(a.dep!=b.dep)return a.dep<b.dep;
    if(a.V.size()!=b.V.size())return a.V.size()<b.V.size();
    for(int i=0; i<a.V.size(); i++)
    {
        if(a.V[i]>b.V[i])
        {
            return 1;
        }
        else if(a.V[i]<b.V[i])
        {
            return 0;
        }
    }
    return 0;
}
void play()
{
    if(strlen(ch[0]+1)!=strlen(ch[1]+1))
    {
        printf("different\n");
        return;
    }
    len=strlen(ch[0]+1);
    for(int i=0; i<=len/2; i++)
    {
        JLJ[0][i].dep=0;
        JLJ[0][i].V.clear();
        JLJ[1][i].dep=0;
        JLJ[1][i].V.clear();
    }
    idx=1;
    st=0;
    p=0;
    DFS(0,0);
    idx=1;
    st=0;
    p=1;
    DFS(0,0);
    sort(JLJ[0],JLJ[0]+st+1,cmp);
    sort(JLJ[1],JLJ[1]+st+1,cmp);
    //cout<<st<<endl;
    for(int i=0; i<=st; i++)
    {
        if(JLJ[0][i].dep!=JLJ[1][i].dep)
        {
            printf("different\n");
            return;
        }
        if(JLJ[0][i].V.size()!=JLJ[1][i].V.size())
        {
            printf("different\n");
            return;
        }
        for(int j=0; j<JLJ[0][i].V.size(); j++)
        {
            if(JLJ[0][i].V[j]!=JLJ[1][i].V[j])
            {
                printf("different\n");
                return;
            }
        }

    }
    printf("same\n");
}
int n;
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        init();
        play();
    }
    //cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 3665 Yukari's Birthday

飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4888

分析:起先想要把所有可能求出来。。。然后二分答案。。。但是内存不够呀然后各种优化啊。。。还是不行啊。。。果然是彩笔~接着就想着暴力。。。暴力你妹啊!!!各种TLE啊~~~没办法了。。想着在线二分。。。但是二分k还是r呢。。。第一直觉是k。。然后就往k里面想了。。。发现k无法正常二分。。。必需要有什么东西可以限制k的大小就可以二分。。。想想r不是很小么40...那就用r来限制k二分。。。接着就是各种悲剧渣死了。。。各种Wa。。。后来看了别人的题解。。。发现我二分里面有些是多余的。。。果然是自己贱啊但是想法还是正确的。。最后改了几个地方就AC了

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#define LL long long
using namespace std;
LL n;
LL cal(int e)
{
    LL l=2,r=min(n,1000000LL);
    while(l<=r)
    {
        LL mid=(l+r)/2;
        LL over=1;
        LL tm=1;
        for(int i=1; i<=e; i++)
        {
            tm=tm*mid;
            over+=tm;
            if(over>n+1)break;
        }
        if(over==n+1||over==n)return mid;
        if(over>n+1)r=mid-1;
        else
        {
            l=mid+1;
        }
    }
    return 0;
}
void play()
{
    LL a=1,b=n-1;
    for(int i=2; i<=39; i++)
    {
        LL t=cal(i);
        if(t==0)continue;
        if(t*i<a*b)
        {
            a=i;
            b=t;
        }
        else if(t*i==a*b)
        {
            if(a>i)
            {
                a=i;
                b=t;
            }
        }
    }
    printf("%lld %lld\n",a,b);
}
int main()
{

    while(scanf("%lld",&n)!=EOF)
    {
        play();
    }
    return 0;
}

ZOJ Problem Set - 3422 Go Deeper

飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4106

今天起来一道2-sat。。。巩固知识~~~

分析:题目的意思就是说求函数最大能递归到第几层。。。告诉你a,b,c数组。。。x数组未知。。。题目秒懂。。。那个程序就不解释了,ACMer肯定能够看得懂:).这种题该怎么搞??其实这里发现一个限制递归的地方就是

if dep < m and x[a[dep]] + x[b[dep]]
 != c[dep] then go(dep + 1, n, m)

那个<m就不用讲了。。如果>=m就溢出a,b,c数组了。。。那么其实就是后面的那个条件。。。我们二分0~(m-1)。假设当前的值是mid。。。那么就可以加边了。。。循环从0~mid。。。取出a[k]和b[k],c[k]的值(语文有限。。。望看懂~)那么如果c[k]的值是0.那么x[a[k]]+x[b[k]]的值至少有一个是1 如果是c[k]是1 那么两个值必须相同。。。如果是2 那么两个值肯定不同。。这里发现其实这个跟那个位运算2-sat题型差不多。。。然后把边添加了跑一下tarjan。。。如果不行。。说明递归层太深、、、r=mid-1.如果可以,那么l=mid+1...这样找到最大值即可~啪啪啪。。。TLE(vector忘记清0了。。。bug啊!!!)最后只要把深度+1就可以AC了

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#define LL long long
using namespace std;
const int maxn=456;
const int Maxn=12345;
struct he
{
    int a,b,c;
} JLJ[Maxn];
vector<int> V[maxn];
int n,m;
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=0; i<m; i++)
    {
        scanf("%d%d%d",&JLJ[i].a,&JLJ[i].b,&JLJ[i].c);
    }
}
bool instack[maxn];
int dfn[maxn],low[maxn],sta[maxn],belong[maxn],indexx,scnt,cntnum;
void tarjan(int u)
{
    dfn[u]=low[u]=indexx++;
    instack[u]=1;
    sta[++scnt]=u;
    for(int i=0; i<V[u].size(); i++)
    {

        int v=V[u][i];
        if(dfn[v]==-1)
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])low[u]=min(dfn[v],low[u]);
    }
    if(dfn[u]==low[u])
    {
        for(int v=-1; v!=u; scnt--)
        {
            v=sta[scnt];
            instack[v]=0;
            belong[v]=cntnum;
        }
        cntnum++;
    }
}
void play()
{
    int l=0;
    int r=m-1;
    int over=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        for(int i=0;i<=2*n;i++)V[i].clear();
        for(int i=0; i<=mid; i++)
        {
            int u=JLJ[i].a*2;
            int v=JLJ[i].b*2;
            int t=JLJ[i].c;
            if(t==2)
            {
                V[u].push_back(v^1);
                V[v].push_back(u^1);
            }
            else if(t==1)
            {
                V[u].push_back(v);
                V[v].push_back(u);
                V[u^1].push_back(v^1);
                V[v^1].push_back(u^1);
            }
            else
            {
                V[u^1].push_back(v);
                V[v^1].push_back(u);
            }
        }
        indexx=cntnum=0;
        scnt=-1;
        memset(dfn,-1,sizeof(dfn));
        memset(instack,0,sizeof(instack));
        for(int i=0; i<2*n; i++)
        {
            if(dfn[i]==-1)tarjan(i);
        }
        bool flag=1;
        for(int i=0; i<n; i++)
        {
            if(belong[i*2]==belong[i*2+1])
            {
                flag=0;
                break;
            }
        }
        if(!flag)
        {
            r=mid-1;
            continue;
        }
        over=max(over,mid);
        l=mid+1;
    }
    printf("%d\n",over+1);

}
int T;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 3717 Balloon

飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5040

从这一题我对2-sat有更深的了解了。。。起先以为2-sat要添加一定可行的边。。。其实不然。。。要求添加可能可以的边。。。意思就是说两个组每个组2个人A1 A2,B1,B2 如果A1选了不能选B1,那么不管A2选了能不能选B1。。。都添加A2 B1这条边搞了一天2-sat 什么鬼~~~反正现在深入了解了。。。好了。。直接正题~~~

分析。。这题就是说给你n个组。每个组有两个球。。每个球有坐标。。从每个组里选择一个球。。且所有球不重叠。。。球最大球径是多少。。。其实就是2-sat问题。。。怎么搞呢??先求出每队之间的距离。。。即一共有四个距离。。。即(A1,B1),(A1,B2),(A2,B1),(A2,B2) 三维距离很简单。。。((x1-x2)^2+(y1-y2)^2+(z1-z2)^2)^0.5即可。。搜噶然后二分球径。。。这里可以剪下枝。。。怎么搞呢。。。先从上面的距离选出最大的距离作为r,l为0。。。然后在循环里面可以判断下那四个距离是否都小于R,如果小于R。。则不符合条件。。直接继续二分。。。如果有一个可以则。。。根据2-sat建边。。。这里得知道要添加可能的边(起先不知道。。Wa到死)一共有四种可能。。所以有4种添边方案。。。然后每次二分跑一下tarjan。。。如果符合条件。。则l变为mid+e...这里e是定义的精度。。。如果不可以。。那r就要变为mid-e...当可行的时候。。。可以定义一个double记录最大符合条件的球径。。。其实起先我还卡了一下为什么距离要跟球径的两倍比较。。。后来想了一下球可以转的啊。。。太菜了。。。最后精度又是个问题。。。但是只要保留4位小数就可以了。。。因为是spj。。。然后啪啪啪!!!AC!!!

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#define LL long long
using namespace std;
const int maxn=456;
const double e=0.0001;
struct he
{
    int a,b,c;
} JLJ[maxn];
vector<int> V[maxn];
double dist[maxn][maxn];
int n;
void init()
{
    for(int i=0; i<n; i++)
    {
        scanf("%d%d%d",&JLJ[i*2].a,&JLJ[i*2].b,&JLJ[i*2].c);
        scanf("%d%d%d",&JLJ[i*2+1].a,&JLJ[i*2+1].b,&JLJ[i*2+1].c);
    }

}
bool instack[maxn];
int dfn[maxn],low[maxn],sta[maxn],belong[maxn],indexx,scnt,cntnum;
void tarjan(int u)
{
    dfn[u]=low[u]=indexx++;
    instack[u]=1;
    sta[++scnt]=u;
    for(int i=0; i<V[u].size(); i++)
    {

        int v=V[u][i];
        if(dfn[v]==-1)
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])low[u]=min(dfn[v],low[u]);
    }
    if(dfn[u]==low[u])
    {
        for(int v=-1; v!=u; scnt--)
        {
            v=sta[scnt];
            instack[v]=0;
            belong[v]=cntnum;
        }
        cntnum++;
    }
}
double cal(double a,double b,double c,double d,double e,double f)
{
    return sqrt((a-d)*(a-d)+(b-e)*(b-e)+(c-f)*(c-f));
}
void play()
{
    double MAX=0;
    for(int i=0; i<n; i++)
    {
        for(int j=i+1; j<n; j++)
        {
            dist[i*2][j*2]=cal(JLJ[i*2].a,JLJ[i*2].b,JLJ[i*2].c,JLJ[j*2].a,JLJ[j*2].b,JLJ[j*2].c);
            dist[i*2][j*2+1]=cal(JLJ[i*2].a,JLJ[i*2].b,JLJ[i*2].c,JLJ[j*2+1].a,JLJ[j*2+1].b,JLJ[j*2+1].c);
            dist[i*2+1][j*2]=cal(JLJ[i*2+1].a,JLJ[i*2+1].b,JLJ[i*2+1].c,JLJ[j*2].a,JLJ[j*2].b,JLJ[j*2].c);
            dist[i*2+1][j*2+1]=cal(JLJ[i*2+1].a,JLJ[i*2+1].b,JLJ[i*2+1].c,JLJ[j*2+1].a,JLJ[j*2+1].b,JLJ[j*2+1].c);
            MAX=max(MAX,dist[i*2][j*2]);
            MAX=max(MAX,dist[i*2][j*2+1]);
            MAX=max(MAX,dist[i*2+1][j*2]);
            MAX=max(MAX,dist[i*2+1][j*2+1]);
        }
    }
    double l=0;
    double r=MAX;
    double over=0;
    while(l<=r)
    {
        double mid=(l+r)/2;
        double d=mid*2;
        bool flag=1;
        for(int i=0; i<=2*n; i++)V[i].clear();
        for(int i=0; i<n; i++)
        {  if(!flag)break;
            for(int j=i+1; j<n; j++)
            {
                if(dist[i*2][j*2]<d&&dist[i*2+1][j*2]<d&&dist[i*2][j*2+1]<d&&dist[i*2+1][j*2+1]<d)
                {
                    flag=0;
                    break;
                }
                if(dist[i*2][j*2+1]<d)
                {
                    V[i*2].push_back(j*2);
                    V[j*2+1].push_back(i*2+1);
                }
                if(dist[i*2][j*2]<d)
                {
                        V[i*2].push_back(j*2+1);
                        V[j*2].push_back(i*2+1);
                }
                if(dist[i*2+1][j*2+1]<d)
                {
                    V[i*2+1].push_back(j*2);
                    V[j*2+1].push_back(i*2);
                }
                if(dist[i*2+1][j*2]<d)
                {
                    V[i*2+1].push_back(j*2+1);
                    V[j*2].push_back(i*2);
                }

            }
        }
        if(!flag){
            r=mid-e;
            continue;
        }
        indexx=cntnum=0;
        scnt=-1;
        memset(dfn,-1,sizeof(dfn));
        memset(instack,0,sizeof(instack));
        for(int i=0; i<2*n; i++)
        {
            if(dfn[i]==-1)tarjan(i);
        }
        for(int i=0; i<n; i++)
        {
            if(belong[i*2]==belong[i*2+1])
            {
                flag=0;
                break;
            }
        }
        if(!flag)
        {
            r=mid-e;
            continue;
        }
        over=max(over,l);
        l=mid+e;

    }
    printf("%.4lf\n",over);
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}