Chieh's Blog

Because Of Coding

ZOJ Problem Set - 3656 Bit Magic

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

分析:搞了一天的2-sat了。。。感觉还是迷迷糊糊真是菜比~~~好了。。。不扯了。。。直接正题:

题意:题意就是给你个矩阵。。。然后求是否有数组满足那段程序的。。。程序自己看啊。。。请点击飞机票。。。这种题怎么变成2-sat。。。烧脑烧力啊。。。,先预处理对角线是否为0和对称点是否一样。。然后只要把每个位单独处理即可~~~。。。坦白地说:对于矩阵的一个元素b[i][j]。。。如果i%2==0&&j%2==0则a[i]&a[j]=b[i][j]。。。这里就可以用到前面文章的内容了。。。就是说a[i]的第k为&a[j]的第k位=b[i][j]的第k位(注:2进制下)然后运用前面文章的信息。不妨设当前点i为1则是i*2,为0则是i*2+1那么就可以建图了

AND 结果为1:建边 x*2+1->x*2,y*2+1->y*2 (两个数必须全为1)

AND 结果为0:建边 y*2->x*2+1,x*2->y*2+1 (两个数至少有一个为0)

OR 结果为1:建边 x*2+1->y*2,2*y+1->x*2 (两个数至少有一个为1)

OR 结果为0:建边 x*2->x*2+1,y*2->y*2+1 (两个数必须全为0)

XOR 结果为1:建边 x*2->y*2+1,y*2->x*2+1,y*2+1->x*2,x*2+1->y*2 (两个数必须不同)

XOR 结果为0:建边 x*2->y*2,y*2->x*2,x*2+1->y*2+1,y*2+1->x*2+1 (两个数必须相同)

这样要处理最多30次。。。因为b[i][j]有范围限制,然后每一次都跑一遍。。。只要有不符合就直接啪啦:NO,如果都想。。YES!啪啪啪~但是速度不快啊。。。不过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=1234;
vector<int> V[maxn];
int ai[567][567];
int n;
void init()
{
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<n; j++)
        {
            scanf("%d",&ai[i][j]);
        }
    }

}
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()
{
    for(int i=0; i<n; i++)
    {
        if(ai[i][i]!=0)
        {
            printf("NO\n");
            return;
        }
    }
    int MAX=0;
    for(int i=0; i<n; i++)
    {
        for(int j=i+1; j<n; j++)
        {   MAX=max(MAX,ai[i][j]);
            if(ai[i][j]!=ai[j][i])
            {
                printf("NO\n");
                return;
            }
        }
    }
    int o=(1<<30);
    while(o>MAX){
        o=o>>1;
    }
    for(int i=1; i<=100; i++)
    {
        if(o==0)break;
        for(int j=0; j<2*n; j++)V[j].clear();
        bool ju=0;
        for(int j=0; j<n; j++)
        {
            for(int k=0; k<n; k++)
            {
                if(j==k)continue;
                int u=(j)*2;
                int v=(k)*2;
                int c=0;
                if(ai[j][k]-o>=0)
                {
                    ju=1;
                    ai[j][k]-=o;
                    c=1;
                }
                if(j%2==0&&k%2==0)
                {
                    if(c==0)
                    {
                        V[u].push_back(v^1);
                        V[v].push_back(u^1);
                    }
                    else
                    {
                        V[u^1].push_back(u);
                        V[v^1].push_back(v);
                    }
                }
                else if(j%2==1&&k%2==1)
                {
                    if(c==0)
                    {
                        V[u].push_back(u^1);
                        V[v].push_back(v^1);
                    }
                    else
                    {
                        V[u^1].push_back(v);
                        V[v^1].push_back(u);
                    }
                }
                else
                {
                    if(c==0)
                    {
                        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].push_back(v^1);
                        V[v].push_back(u^1);
                        V[v^1].push_back(u);
                        V[u^1].push_back(v);
                    }
                }
            }
        }
        o=o>>1;
        if(!ju)
        {
            continue;
        }
        indexx=cntnum=0;
        scnt=-1;
        memset(dfn,-1,sizeof(dfn));
        memset(instack,0,sizeof(instack));
        for(int j=0; j<2*n; j++)
        {
            if(dfn[j]==-1)tarjan(j);
        }
        for(int j=0; j<n; j++)
        {
            if(belong[j*2]==belong[j*2+1])
            {
                printf("NO\n");
                return;
            }
        }
    }
    printf("YES\n");
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}

POJ 3678 Katu Puzzle

飞机票:http://poj.org/problem?id=3678

分析:题意。。。就是说给你个有向图,然后给你变a b 且a b有个值为c 要求a的值op b的值=c 其中op为(&,|,^)位运算,求解是否可以求出这样的解。继续2-sat。。。这题怎么搞呢。。。其实只要知道为位运算相互间的限制然后建图求解即可。。。那么关系是什么呢。。。不妨设当前点i为1则是i*2,为0则是i*2+1那么就可以建图了

AND 结果为1:建边 x*2+1->x*2,y*2+1->y*2 (两个数必须全为1)

AND 结果为0:建边 y*2->x*2+1,x*2->y*2+1 (两个数至少有一个为0)

OR 结果为1:建边 x*2+1->y*2,2*y+1->x*2 (两个数至少有一个为1)

OR 结果为0:建边 x*2->x*2+1,y*2->y*2+1 (两个数必须全为0)

XOR 结果为1:建边 x*2->y*2+1,y*2->x*2+1,y*2+1->x*2,x*2+1->y*2 (两个数必须不同)

XOR 结果为0:建边 x*2->y*2,y*2->x*2,x*2+1->y*2+1,y*2+1->x*2+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=2345;
vector<int> V[maxn];
int n,m;
char ch[100];
void init()
{
    for(int i=0; i<=2*n; i++)V[i].clear();
    int u,v,c;
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d%s",&u,&v,&c,ch+1);
        u=u*2;
        v=v*2;
        if(ch[1]=='A')
        {
            if(c==0)
            {
                V[u].push_back(v^1);
                V[v].push_back(u^1);
            }
            else
            {
                V[u^1].push_back(u);
                V[v^1].push_back(v);
            }
        }
        else if(ch[1]=='O')
        {
            if(c==0)
            {
                V[u].push_back(u^1);
                V[v].push_back(v^1);
            }
            else
            {
                V[u^1].push_back(v);
                V[v^1].push_back(u);
            }
        }
        else
        {
            if(c==0)
            {
                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].push_back(v^1);
                V[v].push_back(u^1);
                V[v^1].push_back(u);
                V[u^1].push_back(v);
            }

        }
    }
}
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()
{
    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])
        {
            printf("NO\n");
            return;
        }
    }
    printf("YES\n");
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}

POJ 3207 Ikki's Story IV - Panda's Trick

飞机票: http://poj.org/problem?id=3207

分析:题目有点难懂。。。但还是2-sat 。。题目的意思就是按顺时针给定圆上n个点。标号0~n-1.然后m个连接。。。注意:连接可以连接在圆内也可以在圆外。。。这里提示我们不一定是直线。。。然后判断是否可以不相交在圆内或圆外。。可以相交在圆上。。。

题目都看晕了。。。其实很简单。。。假如当前是的i个连接且坐标为li ri,如果有个连接k。且lk>li&&lk<ri&&rk>ri 或则

rk>li&&rk<lr&&lk<li 这样就代表这两条不能在一侧。否则相交。。画个图你就懂了

这里有个判断我很喜欢

  int cnt=0;
  if((JLJ[i].l>JLJ[j].l&&JLJ[i].l<JLJ[j].r))cnt++;
  if((JLJ[i].l>JLJ[j].l&&JLJ[i].r<JLJ[j].r))cnt++;

如果cnt=1就相交。。。为什么呢cnt=1时就说明前面满足了一个条件。另一个不满足。。就是说只有一个点在里面。另个在外面。。怎么画肯定都相交。。。简单易懂。。然后就是2-sat 啪啪啪~~~oh,no!好像还有个问题。。就是这里要添加4条边。。因为互相制约。。然后就可以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=1234;
vector<int> V[maxn];
int n,m;
struct he
{
    int l,r;
} JLJ[567];
void init()
{
    for(int i=0; i<=2*m; i++)V[i].clear();
    int u,v;
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&u,&v);
        if(u>v)swap(u,v);
        JLJ[i].l=u;
        JLJ[i].r=v;
    }
    for(int i=1; i<=m; i++)
    {
        for(int j=i+1; j<=m; j++)
        {
            int cnt=0;
            if((JLJ[i].l>JLJ[j].l&&JLJ[i].l<JLJ[j].r))cnt++;
            if((JLJ[i].l>JLJ[j].l&&JLJ[i].r<JLJ[j].r))cnt++;
            if(cnt==1)
            {
                int u=2*(i-1);
                int v=2*(j-1);
                V[u].push_back(v+1);
                V[u+1].push_back(v);
                V[v].push_back(u+1);
                V[v+1].push_back(u);
            }
        }
    }
}
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()
{
    indexx=cntnum=0;
    scnt=-1;
    memset(dfn,-1,sizeof(dfn));
    memset(instack,0,sizeof(instack));
    for(int i=0; i<2*m; i++)
    {
        if(dfn[i]==-1)tarjan(i);
    }
    for(int i=0; i<m; i++)
    {
        if(belong[i*2]==belong[i*2+1])
        {
            printf("the evil panda is lying again\n");
            return;
        }
    }
    printf("panda is telling the truth...\n");
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}

HDU 1824 Let's go home

飞机票: http://acm.hdu.edu.cn/showproblem.php?pid=1824

其实这个题意好像有点模糊。。。比如第一个样例。。。如果0留下,那么1肯定要回家。。。则2肯定要留下。。。或则是1留下。。。那么2和0肯定要回家。。。或则是2留下,那么1肯定要回家,0肯定要留下。。。然而题意的意思是,0留下则1,2都的回家。或则1,2留下,0回家。。。而情况就只有 0,2回家,1留下,0,2留下,1回家, 跟题意不符。。。但是网上说是2-sat了。。那就2-sat吧。练练手。。。其实很简单。。。只要把两名队员看成一个正题就可以解决了

/*
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=2345;
vector<int> V[maxn];
int can[maxn*2];
int n,m;
void init()
{
    for(int i=0; i<=2*n; i++)V[i].clear();
    int o,p,q;
    for(int i=0; i<n; i++)
    {
        scanf("%d%d%d",&o,&p,&q);
        can[o]=i*2;
        can[p]=i*2+1;
        can[q]=i*2+1;
    }
    int u,v;
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&u,&v);
        V[u].push_back(v^1);
        V[v].push_back(u^1);
    }
}
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()
{
    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])
        {
            printf("no\n");
            return;
        }
    }
    printf("yes\n");
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}

 

HDU 3062 Party

飞机票: http://acm.hdu.edu.cn/showproblem.php?pid=3062

开始学习2-sat了

其实2-sat原理很简单,简单来说,有2个组。每个组里有2个人。定义为A1,A2,B1,B2.如果选择A1不能选择B1。则在A1,B2连一条边,A2,B1连一条边。。。然后强联通一下。判断下A1和A2是否属于同一个块。如果是的话,那方案不可行,不是的话反之~然后这题是个入门题。把没对夫妻分为2*i和 2*i+1 然后建边,tarjan一下。。判断2*i和2*i+1是否属于同一个块即可。。。

/*
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=2345;
vector<int> V[maxn];
int n,m;
void init()
{
    for(int i=0; i<=2*n; i++)V[i].clear();
    int a,b,c,d,u,v;
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        u=2*a+c;
        v=2*b+d;
        V[u].push_back(v^1);
        V[v].push_back(u^1);
    }
}
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()
{
    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])
        {
            printf("NO\n");
            return;
        }
    }
    printf("YES\n");
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}

HDU 5154 Harry and Magical Computer

飞机票: http://acm.hdu.edu.cn/showproblem.php?pid=5154

很好理解的题。。。

题目分析:最多100个点 跑一边floyd 看是否有环 复杂度 0(n^3)。一个trick就是自己到自己有环也是NO

/*
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=123;
int ai[maxn][maxn];
int n,m;
void init()
{
    int u,v;
    memset(ai,0,sizeof(ai));
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&u,&v);
        ai[u][v]=1;
    }
}
void play()
{
    for(int k=1; k<=n; k++)
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(ai[i][k]&&ai[k][j])
                {
                    ai[i][j]=1;
                }
            }
        }
    }   
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(ai[i][j]&&ai[j][i])
            {
                printf("NO\n");
                return;
            }
        }
    }

    printf("YES\n");
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        play();
    }
    return 0;
}

ZOJ Problem Set - 3183 Counting Binary Trees

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3273

自从湖南旅游回来之后整个人真的是不佳啊~~~一天就做了这么一个题。。。感觉自己的越来越弱了。。。好了进入正题

分析:首先,这种题先找规律

当n=1时,只有1个根节点,则只能组成1种形态的二叉树,令n个节点可组成的二叉树数量表示为h(n),
则h(1)=1; 
当n=2时,1个根节点固定,还有n-1个节点,可以作为左子树,也可以作为右子树, 即:h(2)=h(0)*h(1)+h(1)*h(0)=2,则能组成2种形态的二叉树。这里h(0)表示空,所以只能算一种形态,即h(0)=1; 
当n=3时,1个根节点固定,还有n-1=2个节点,可以在左子树或右子树, 即:h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=5,则能组成5种形态的二叉树。 
以此类推,当n>=2时,可组成的二叉树数量为h(n)=h(0)*h(n-1)+h(1)*h(n-2)+...+h(n-1)*h(0)种。
 
即符合Catalan数的定义,可直接利用通项公式得出结果。 递归式: 
  h(n)=h(n-1)*(4*n-2)/(n+1);  该递推关系的解为: 
  h(n)=C(2n,n)/(n+1) (n=1,2,3,...)

然后就啪啪啪、、、Wa了尼玛。。。作为弱者的我不知道怎么错了。。。后来看看递推里面带了除法。。。我去。。。我不会带除法的取余果断百度了一下。。。soga。。看了下要变乘法。。。ok直接套取余模版。。。然后就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=123456;
int n,m;
//LL ai[maxn];
int sm[1000],p;//将m分解质因数
int sa[1000];//4*i-2 和 i+1 分解质因数
//素数筛选
int flag[50000],pri[50000],pl;
void prime()
{
    for(int i=2; i<50000; i++)
    {
        if(flag[i]==0) pri[pl++]=i;
        for(int j=0; j<pl&&i*pri[j]<50000; j++)
        {
            flag[i*pri[j]]=1;
            if(i%pri[j]==0) break;
        }
    }
}
int extgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    int r=extgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return r;
}
LL over;
void init()
{
    over=1%m;
    LL res=1;//n=1时 sum=1 后面从2开始
    p=0;
    int tm=m;//将m分解质因数
    for(int i=0; pri[i]*pri[i]<=tm; i++)
    {
        if(tm%pri[i]==0)
        {
            sm[p++]=pri[i];
            while(tm%pri[i]==0) tm/=pri[i];
        }
    }
    if(tm>1) sm[p++]=tm;//important
    memset(sa,0,sizeof(sa));
    for(int i=2; i<=n; i++)
    {
        int t;
        t=4*i-2;
        for(int j=0; j<p; j++)
        {
            while(t%sm[j]==0)
            {
                sa[j]++,t/=sm[j];
            }
        }
        res=res*t%m;
        t=i+1;
        for(int j=0; j<p; j++)
        {
            while(t%sm[j]==0)
            {
                sa[j]--,t/=sm[j];
            }
        }
        if(t>1)
        {
            int x,y;
            int r=extgcd(t,m,x,y);
            x=(x%m+m)%m;
            res=res*x%m;
        }
        LL tmp=res;
        for(int j=0; j<p; j++)
        {
            for(int k=0; k<sa[j]; k++)
            {
                tmp=tmp*sm[j]%m;
            }
        }
        over=(over+tmp)%m;
    }
}
void play()
{

    printf("%lld\n",over);
}
int main()
{
    prime();
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)break;
        init();
        play();
    }
    return 0;
}

ZOJ Problem Set - 3211 Dream City

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3374

题意就是说有n棵树,每棵树有个初始值a个金币,而且每过一天会增加b个金币,求m天最多可以得到金币,而且必须每天都要砍一棵树

刚开始以为是贪心。。。Wa,后来举了个反例才发现自己的贪心是错的。。。那么应该怎么做这道题呢??DP。。。除了贪心我只能想到DP,该怎么DP呢。。。后来想到来个数组dp[i][j]表示前i棵树在j天最大可以产生多少。。。但是有个特别的值。。就是b。。。好像又把我卡住了。。。想一下,就是怎么来确定顺序呢。。。根据b排序。。。把b最小的放前面。。。这样到后面的时候,b已经是最优的了。。。所以这道题的大致感觉就是。当前要放那个哪个地方的话。。。前面的得先放好而且是最优的

 ai[i][j]=max(ai[i-1][j],ai[i-1][j-1]+JLJ[i].b*(j-1)+JLJ[i].a);

这样答案就出来了

/*
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=250+10;
struct he
{
    int a,b;
} JLJ[maxn];
int ai[maxn][maxn];
int n,m;
int T;
bool cmp(he a,he b)
{
    return a.b<b.b;
}
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&JLJ[i].a);
    }
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&JLJ[i].b);
    }
}
void play()
{   sort(JLJ+1,JLJ+1+n,cmp);
    memset(ai,0,sizeof(ai));
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {   if(j<=i)
            ai[i][j]=max(ai[i-1][j],ai[i-1][j-1]+JLJ[i].b*(j-1)+JLJ[i].a);
            else break;
        }
    }
    printf("%d\n",ai[n][m]);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }
    //cout << "Hello world!" << endl;
    return 0;
}

HDU 5150 Sum Sum Sum

链接 http://acm.hdu.edu.cn/showproblem.php?pid=5150

Bestcoder的一道题。。。直接暴力即可。。。但是看见被hack的好多。。。原来是在1这里出了问题。。。后面几个题没几个做的出来。看来这场的题偏难了

/*
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;
int n;
int ai[maxn];
int bi[maxn];
void init()
{
    for(int i=1; i<=n; i++)scanf("%d",&bi[i]);
}
void play()
{
    int over=0;
    for(int i=1; i<=n; i++)
    {
        if(!ai[bi[i]])over+=bi[i];
    }
    printf("%d\n",over);
}
int main()
{
    memset(ai,0,sizeof(ai));
    for(int i=1; i<=1000; i++)
    {
        for(int j=2; j<i; j++)
        {
            if(i%j==0)ai[i]=1;
        }
    }
    while(scanf("%d",&n)!=EOF)
    {
        init();
        play();
    }
    return 0;
}

ZOJ Problem Set - 2505 Sticks

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1505

题目大意:就是有最多1024个魔法棒放在一排,按照时间顺序对第i个魔法棒改变状态,问最长的连续子序列且都是奇数次改变多长。

题目分析:网上说是暴力。。。我暴力了一发。。。过了。。。但是这题明显是线段树啊,虽然暴力出奇迹

怎么个线段法??就是每个区间记录最大值和是否是连续的区间。。怎么定义??一个节点定义lval,rval,link,max,l,r 其中l,r不用说了,lval就是左边的长度,val同理,max最大值,link是否连续,如果连续,那么lva=rval;如果不连续当然也可能相等,但是不可能是区间的长度,这样我们就好搞了,每次更新子节点,则父节点判断是否连续,如果两个子节点都连续,则父节点lval=左节点的lva+右节点的rval,且max就是当前区间长~~~link当然是连接的啊~~不想等呢》》

那好办了,如果左节点是连得,那父节点lval=左节点lval+右节点lval,且rval=右节点rval,link当然为false了,max就是lmax+rmax

如果右节点是连得,同理哈。。。最后一种情况左右都不连。。。那父节点lval=左节点lval,父节点rval=右节点val,max还是lmax+rmax,link必然false,OK了,讨论完毕。。。AC啦


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=4097;
struct he
{
    int l,r,MAX;
    int lval,rval;
    int link;
} JLJ[maxn*2];
void BuildTree(int l,int r,int now)
{
    JLJ[now].l=l;
    JLJ[now].r=r;
    JLJ[now].link=0;
    JLJ[now].MAX=0;
    JLJ[now].lval=0;
    JLJ[now].rval=0;
    if(l==r)return;
    int mid=(l+r)/2;
    BuildTree(l,mid,now*2);
    BuildTree(mid+1,r,now*2+1);

}
void Update(int l,int now)
{
    if(JLJ[now].l==l&&JLJ[now].r==l)
    {
        if(JLJ[now].MAX==0)JLJ[now].MAX=1;
        else JLJ[now].MAX=0;
        JLJ[now].lval=JLJ[now].rval=JLJ[now].MAX;
        if(JLJ[now].MAX==1)
            JLJ[now].link=1;
        else JLJ[now].link=0;
        return;
    }
    int mid=(JLJ[now].l+JLJ[now].r)/2;
    if(l<=mid)
    {
        Update(l,now*2);
    }
    else
    {
        Update(l,now*2+1);
    }
    int j1=JLJ[now*2].link;
    int j2=JLJ[now*2+1].link;
    if(j1&&j2)
    {
        JLJ[now].link=1;
        JLJ[now].lval=JLJ[now].rval= JLJ[now].MAX=JLJ[now*2].MAX+JLJ[now*2+1].MAX;
    }
    else if(j1)
    {
        JLJ[now].link=0;
        JLJ[now].lval=JLJ[now*2].MAX+JLJ[now*2+1].lval;
        JLJ[now].rval=JLJ[now*2+1].rval;
        JLJ[now].MAX=max(JLJ[now].lval,max(JLJ[now*2+1].MAX,JLJ[now*2].MAX));
    }
    else if(j2)
    {   JLJ[now].link=0;
        JLJ[now].lval=JLJ[now*2].lval;
        JLJ[now].rval=JLJ[now*2].rval+JLJ[now*2+1].MAX;
        JLJ[now].MAX=max(JLJ[now*2].MAX,max(JLJ[now].rval,JLJ[now*2+1].MAX));
    }
    else
    {   JLJ[now].link=0;
        JLJ[now].lval=JLJ[now*2].lval;
        JLJ[now].rval=JLJ[now*2+1].rval;
        JLJ[now].MAX=max(JLJ[now*2].MAX,max(JLJ[now*2+1].MAX,JLJ[now*2].rval+JLJ[now*2+1].lval));
    }
}
int T,n;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        BuildTree(1,4096,1);
        int l;
        int MAX=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&l);
            Update(l,1);
            MAX=max(MAX,JLJ[1].MAX);
        }
        printf("%d\n",MAX);
    }
    //cout << "Hello world!" << endl;
    return 0;
}