2016wust暑假集训之题解汇总2


数据结构

hdu4339(树状数组+二分)

题意:给出长度分别为l1和l2的字符串s1和s2,要求进行k次操作,每次操作输入的第一个数如果是1,接着就输入两个整数a,i和一个字符c,要求将第a个字符串的下标为i的字符换位c。如果输入的第一个数是2,紧接着就输入一个数字i,要求找到一个最大的数字j,使得在区间[i,j)中两个字符串的字符相等。
分析:之前看到题目给了10s直接暴力求解TLE了。。。后来从别人的题解中受到了启发,需要用到树状数组,这样能节省不少时间开销。用一个数组保存状态(如果s1[i]=s2[i]就为0,否则为1),每次进行1操作时更新树状数组的数据,而进行2操作时用二分找到满足sum(j)-sum(i)=0的最大j就可以了。这道题又加深了我对树状数组的理解。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000005;
char s1[maxn],s2[maxn];
int c[maxn];
int k,len;
int lowbit(int x)
{
    return x&-x;
}
void update(int i,int x)//更新树状数组的数据
{
    for(;i<=len;i+=lowbit(i))
        c[i]+=x;
}
int sum(int i)//求和操作
{
    int sum=0;
    for(;i>0;i-=lowbit(i))
        sum+=c[i];
    return sum;
}
int Search(int i)//二分查找
{
    int left=i,right=len,mid=0,ans=i-1;
    while(left<=right)
    {
        mid=(left+right)/2;
        int x=sum(mid)-sum(i-1);
        //cout<<"mid="<<mid<<",sum(mid)="<<sum(mid)<<",sum(i-1)="<<sum(i-1)<<endl;
        if(x==0) { ans=mid; left=mid+1; }
        else right=mid-1;
    }
    return ans-i+1;
}
int main()
{
    int T;
    cin>>T;
    int cnt=1;
    while(T--)
    {
        memset(c,0,sizeof c);
        printf("Case %d:\n",cnt++);
        scanf("%s%s",s1,s2);
        scanf("%d",&k);
        int len1=strlen(s1),len2=strlen(s2);
        len=min(len1,len2);
        for(int i=0;i<len;i++)
        {
            if(s1[i]!=s2[i])
            {
                update(i+1,1);
            }
        }
        /*for(int i=1;i<=len;i++)
            printf("sum%d=%d  ",i,sum(i));*/
        while(k--)
        {
            int t;
            scanf("%d",&t);
            if(t==1)
            {
                int a,i;
                char ch;
                scanf("%d %d %c",&a,&i,&ch);
                bool flag=(s1[i]==s2[i]);
                if(a==1) s1[i]=ch;
                else s2[i]=ch;
                if(flag&&s1[i]!=s2[i])
                {
                    update(i+1,1);
                }
                if((!flag)&&s1[i]==s2[i])
                {
                    update(i+1,-1);
                }
            }
            else
            {
                int i;
                scanf("%d",&i);
                int ans=Search(i+1);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}
View Code

hdu5737(有序表线段树)

解析:这是我以前没有接触过的线段树类型,有序表线段树,每个节点申请了两段空间,主要是为了保存左边儿子会有多少比v小的,右边儿子会有多少比v小

的,所以在建树过程中要归并排序。可能我讲起来比较难懂,详见代码,我给了注释。

代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef __int64 LL;
#define e tree[id]
#define lson tree[id*2]
#define rson tree[id*2+1]
const int mod=1e9+7;
const int maxn=100005;
const int C=~(1<<31),M=(1<<16)-1;
int rnd(int last,int &a,int &b)
{
    a=(36969+(last>>3))*(a&M)+(a>>16);
    b=(18000+(last>>3))*(b&M)+(b>>16);
    return (C&((a<<16)+b))%1000000000;
}
int n,m,A,B,a[maxn],b[maxn],c[maxn],xs[maxn];
bool cmp(const int& x,const int& y){ return b[x]<b[y]; }
int data[maxn<<6],*p;
int* New(int len){ p+=len; return p-len; } //静态的申请空间
void init() //离散化
{
    for(int i=0;i<n;i++) xs[i]=b[i];
    sort(xs,xs+n);
    for(int i=0;i<n;i++) b[i]=upper_bound(xs,xs+n,b[i])-xs-1;
    for(int i=0;i<n;i++) a[i]=upper_bound(xs,xs+n,a[i])-xs-1;
    p=data; //在最开始的位置
}
struct Tree
{
    int le,ri,d,sum;
    int *lid,*rid;
    void Set(int v){ d=v; sum=v+1; }
    int GoLe(int v){ return v==-1?v:lid[v]; }  //左边有多少<=v的
    int GoRi(int v){ return v==-1?v:rid[v]; }  //右边有多少<=v的
}tree[4*maxn];
void pushup(int id){ e.sum=lson.sum+rson.sum; } //取和
void pushdown(int id)
{
    if(e.d!=-2)  //延迟更新
    {
        lson.Set(e.GoLe(e.d));
        rson.Set(e.GoRi(e.d));
        e.d=-2;
    }
}
void Build_tree(int id,int le,int ri)
{
    e.le=le,e.ri=ri,e.d=-2;
    e.lid=New(ri-le+1);  //左右都要申请空间
    e.rid=New(ri-le+1);
    if(le==ri){ e.sum=(a[le]>=b[le]); return; }
    int mid=(le+ri)/2;
    Build_tree(id*2,le,mid);
    Build_tree(id*2+1,mid+1,ri);
    pushup(id);
    int ll=mid-le+1,rl=ri-mid;
    int *vl=b+le,*vr=b+mid+1;
    int i=0,j=0,cnt=0;
    while(i<ll&&j<rl)  //归并排序
    {
        if(vl[i]<vr[j])  //左边小于右边
        {
            e.lid[cnt]=i;
            e.rid[cnt]=j-1;
            c[cnt++]=vl[i++];
        }
        else
        {
            e.lid[cnt]=i-1;
            e.rid[cnt]=j;
            c[cnt++]=vr[j++];
        }
    }
    while(i<ll)  //左边没完的
    {
        e.lid[cnt]=i;
        e.rid[cnt]=j-1;
        c[cnt++]=vl[i++];
    }
    while(j<rl)  //右边没完的
    {
        e.lid[cnt]=i-1;
        e.rid[cnt]=j;
        c[cnt++]=vr[j++];
    }
    int k=0;
    for(int i=le;i<=ri;i++) b[i]=c[k++];
}
void Update(int id,int x,int y,int v) //更新
{
    int le=e.le,ri=e.ri;
    if(x<=le&&ri<=y){ e.Set(v); return; } //在区间内
    pushdown(id);
    int mid=(le+ri)/2;
    if(x<=mid) Update(id*2,x,y,e.GoLe(v)); //左边GoLe代表左边有多少<=v的
    if(y>mid) Update(id*2+1,x,y,e.GoRi(v));//右边同理
    pushup(id);
}
int Query(int id,int x,int y)  //查询
{
    int le=e.le,ri=e.ri;
    if(x<=le&&ri<=y) return e.sum;  //在区间内直接返回值
    pushdown(id);  //延迟更新
    int mid=(le+ri)/2;
    int ret=0;
    if(x<=mid) ret+=Query(id*2,x,y);   //加上左边
    if(y>mid)  ret+=Query(id*2+1,x,y); //加上右边
    return ret;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d",&n,&m,&A,&B);
        for(int i=0;i<n;i++) scanf("%d",&a[i]); //输入
        for(int i=0;i<n;i++) scanf("%d",&b[i]);
        init();
        Build_tree(1,0,n-1); //建树
        int last=0,ret=0;
        for(int i=1;i<=m;i++)
        {
            int l=rnd(last,A,B)%n;
            int r=rnd(last,A,B)%n;
            int x=rnd(last,A,B)+1;
            if(l>r) swap(l,r);
            if((l+r+x)%2)  //为奇数是插入
            {
                x=upper_bound(xs,xs+n,x)-xs-1;
                Update(1,l,r,x);
            }
            else  //否则查询
            {
                last=Query(1,l,r);
                ret=(ret+(LL)i*last%mod)%mod;
            }
        }
        printf("%d\n",ret);
    }
    return 0;
}
View Code

hdu5381(莫队算法)

解析: 莫队,先预处理出以i为右端点的区间的gcd值,有一些连续的区间的gcd值是相同的,比如[j,i],[j+1,i],[j+2,i]的gcd值是相同的,我们可以把[j,j+2]这个
区间保存下来。同时也预处理出以i为左端点的,最后用莫队算法。详见代码实现。
代码
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=10005;
typedef __int64 LL;
int N,M,A[maxn],block;
struct Ques
{
    int x,y,id;
    Ques(int x=0,int y=0,int id=0):x(x),y(y),id(id){}
    bool operator < (const Ques& t) const
    {
        int a=x/block,b=t.x/block;  //排序
        if(a!=b) return a<b;
        return y<t.y;
    }
}ques[maxn];
int gcd(int a,int b){ return b==0?a:gcd(b,a%b); }
struct node
{
    int id,g;
    node(int id=0,int g=0):id(id),g(g){}
};
vector<node> vl[maxn],vr[maxn];
void GetLe()
{
    for(int i=1;i<=N;i++)  //得到以i为右端点连续区间的gcd值
    {
        if(i==1){ vl[i].push_back(node(i,A[i])); continue; }
        int g=A[i],id=i;
        int Size=vl[i-1].size();
        for(int j=0;j<Size;j++)
        {
            node& t=vl[i-1][j];
            int ng=gcd(t.g,g);
            if(ng!=g) vl[i].push_back(node(id,g));
            g=ng; id=t.id;
        }
        vl[i].push_back(node(id,g));
    }
}
void GetRi()  //同理
{
    for(int i=N;i>=1;i--)
    {
        if(i==N){ vr[i].push_back(node(i,A[i])); continue; }
        int g=A[i],id=i;
        int Size=vr[i+1].size();
        for(int j=0;j<Size;j++)
        {
            node& t=vr[i+1][j];
            int ng=gcd(t.g,g);
            if(ng!=g) vr[i].push_back(node(id,g));
            g=ng; id=t.id;
        }
        vr[i].push_back(node(id,g));
    }
}
LL WorkLe(int x,int y)
{
    int Size=vl[y].size();
    int ny=y;
    LL ret=0;
    for(int i=0;i<Size;i++)
    {
        node& t=vl[y][i];
        if(t.id>=x)
        {
            ret+=(LL)t.g*(ny-t.id+1);
            ny=t.id-1; //跳过去
        }
        else{ ret+=(LL)t.g*(ny-x+1); break; }
    }
    return ret;
}
LL WorkRi(int x,int y)
{
    int nx=x;
    LL ret=0;
    int Size=vr[x].size();
    for(int i=0;i<Size;i++)
    {
        node& t=vr[x][i];
        if(t.id<=y)
        {
            ret+=(LL)t.g*(t.id-nx+1);
            nx=t.id+1;
        }
        else { ret+=(LL)t.g*(y-nx+1); break; }
    }
    return ret;
}
LL ans[maxn];
void solve()
{
    for(int i=0;i<=N;i++) vl[i].clear(),vr[i].clear();
    block=(int)sqrt(N+0.5);  //分块
    sort(ques,ques+M);  //排序
    GetLe(); //得到左边连续相同的gcd区间
    GetRi(); //得到右边连续相同的gcd区间
    int x=1,y=0;
    LL ret=0;
    for(int i=0;i<M;i++)  //莫队的主要实现部分
    {
        Ques& q=ques[i];
        while(y<q.y){ y++; ret+=WorkLe(x,y); }
        while(y>q.y){ ret-=WorkLe(x,y); y--; }
        while(x<q.x){ ret-=WorkRi(x,y); x++; }
        while(x>q.x){ x--; ret+=WorkRi(x,y); }
        ans[q.id]=ret; //保存答案
    }
    for(int i=0;i<M;i++) printf("%lld\n",ans[i]); //输出
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        for(int i=1;i<=N;i++) scanf("%d",&A[i]);//输入
        scanf("%d",&M);
        int x,y;
        for(int i=0;i<M;i++)
        {
            scanf("%d%d",&x,&y);
            ques[i]=Ques(x,y,i);  //离线保存查询
        }
        solve();
    }
    return 0;
}
View Code

KMP算法

//参照《算法入门竞赛经典训练指南》
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
void GetFail(char *P,int *fail)
{
    int L=strlen(P);
    fail[0]=fail[1]=0;
    for(int i=1;i<L;i++)
    {
        int j=fail[i];
        while(j&&P[i]!=P[j]) j=fail[j];
        fail[i+1]=(P[i]==P[j]?j+1:0);
    }
}
void KMP(char *T,char *P,int *fail)
{
    int L1=strlen(T);
    int L2=strlen(P);
    GetFail(P,fail);
    int j=0;
    for(int i=0;i<L1;i++)
    {
        while(j&&T[i]!=P[j]) j=fail[j];
        if(T[i]==P[j]) j++;
        if(j==L2) printf("%d\n",i-L2+1);
    }
}
int main()
{
    char T[30],P[20];
    int fail[30];
    while(scanf("%s%s",T,P)!=EOF)
    {
        KMP(T,P,fail);
    }
    return 0;
}
View Code

后缀数组模板

//参照《算法入门竞赛经典训练指南》
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=1005;
int wa[maxn],wb[maxn],wv[maxn],WS[maxn],sa[maxn];
bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; }
void DA(int *r,int n,int m)  //模板
{
    int i,j,p;
    int *x=wa,*y=wb;
    for(i=0;i<m;i++) WS[i]=0;
    for(i=0;i<n;i++) WS[x[i]=r[i]]++;
    for(i=1;i<m;i++) WS[i]+=WS[i-1];
    for(i=n-1;i>=0;i--) sa[--WS[x[i]]]=i;

    for(p=1,j=1;p<n;j<<=1,m=p)
    {
        for(p=0,i=n-j;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0;i<n;i++) wv[i]=x[y[i]];
        for(i=0;i<m;i++) WS[i]=0;
        for(i=0;i<n;i++) WS[wv[i]]++;
        for(i=1;i<m;i++) WS[i]+=WS[i-1];
        for(i=n-1;i>=0;i--) sa[--WS[wv[i]]]=y[i];
        swap(x,y);
        for(p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
}
int a[maxn],b[maxn],rk[maxn],h[maxn];
void GetHeight(int *r,int n)
{
    for(int i=0;i<=n;i++) rk[sa[i]]=i;
    int k=0;
    for(int i=0;i<n;i++)
    {
        if(k) k--;  //先减1
        int j=sa[rk[i]-1];//排名在前面的
        while(r[i+k]==r[j+k]) k++; //相同一直加
        h[rk[i]]=k;
    }
}
int main()
{
    int N;
    while(scanf("%d",&N)!=EOF)
    {
        for(int i=0;i<N;i++) scanf("%d",&a[i]);
        a[N]=0;
        DA(a,N+1,N);
        GetHeight(a,N);
        printf("\n");
    }
    return 0;
}
View Code

ac自动机模板

//参照《算法竞赛入门经典训练指南》
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10005;
const int cs=26;
struct AC
{
    int next[maxn][cs],val[maxn];
    int id;
    int fail[maxn],last[maxn];
    void init(){ memset(next[0],0,sizeof(next[0])); id=0; }//刚开始只初始化根节点
    int GetId(char c){ return c-'a'; }
    void Insert(char *S,int I) //字典树插入字符串
    {
        int s,be=0;
        for(int i=0;S[i]!='\0';i++)
        {
            s=GetId(S[i]);
            if(next[be][s]==0)
            {
                next[be][s]=++id; //增加新节点
                val[id]=0;      //置0
                memset(next[id],0,sizeof(next[id])); //清空
            }
            be=next[be][s];
        }
        val[be]=I;//保存信息
    }
    void GetFail() //找失配指针
    {
        queue<int> que;
        fail[0]=0;
        for(int i=0;i<cs;i++)
        {
            int u=next[0][i];
            fail[u]=last[u]=0;
            if(u) que.push(u);
        }
        while(!que.empty())
        {
            int x=que.front(); que.pop();
            for(int i=0;i<cs;i++)
            {
                int u=next[x][i];
                if(!u){ next[x][i]=next[fail[x]][i]; continue; }
                que.push(u);
                int v=fail[x];
                while(v&&!next[v][i]) v=fail[v];
                fail[u]=next[v][i];
                last[u]=val[fail[u]]?fail[u]:last[fail[u]];
            }
        }
    }
    void Print(int i,int j)
    {
        if(!j) return;
        printf("%d: %d\n",i,val[j]);
        Print(i,last[j]);
    }
    void Find(char *T)  //匹配
    {
        int L=strlen(T);
        int j=0,s;
        for(int i=0;i<L;i++)
        {
            s=GetId(T[i]);
            j=next[j][s];
            if(val[j]) Print(i,j);
            else if(last[j]) Print(i,j);
        }
    }
}ac;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ac.init();
        int N;
        scanf("%d",&N);
        char mom[50],son[10][20];
        scanf("%s",mom);
        for(int i=1;i<=N;i++)
        {
            scanf("%s",son[i]);
            ac.Insert(son[i],i);
        }
        ac.GetFail();
        ac.Find(mom);
    }
    return 0;
}
View Code

poj3277(扫描线)

/*
题意:给出N个矩形,都在同一水平线上,求面积并

解析:裸的扫描线
*/
#include<cstdio> 
#include<cstring> 
#include<string> 
#include<cmath> 
#include<iostream> 
#include<algorithm> 
#include<vector> 
#include<set> 
#include<map> 
#include<stack> 
#include<queue> 
#include<sstream> 
#include<utility> 
#include<iterator> 
using namespace std; 
const int INF=1e9+7; 
const double eps=1e-7; 
typedef __int64 LL; 
const int maxn=80005; 
int N,num,A[maxn]; 
struct node 
{ 
    int L,R,H,s; 
    node(int L=0,int R=0,int H=0,int s=0) 
    :L(L),R(R),H(H),s(s){} 
    bool operator < (const node& t) const
    { 
        return H<t.H; 
    } 
}Nod[maxn]; 
struct Tree 
{ 
    int le,ri,lazy; 
    int sum; 
}tree[4*maxn]; 
void build_tree(int id,int le,int ri) 
{ 
    Tree& e=tree[id]; 
    e.le=le,e.ri=ri,e.lazy=0,e.sum=0; 
    if(le==ri) return; 
    int mid=(le+ri)/2; 
    build_tree(id*2,le,mid); 
    build_tree(id*2+1,mid+1,ri); 
    return; 
} 
void pushup(int id) 
{ 
    Tree& fa=tree[id]; 
    Tree& lson=tree[id*2]; 
    Tree& rson=tree[id*2+1]; 
    if(fa.lazy) fa.sum=A[fa.ri+1]-A[fa.le]; 
    else if(fa.le==fa.ri) fa.sum=0; 
    else fa.sum=lson.sum+rson.sum; 
} 
void update(int id,int x,int y,int c) 
{ 
    Tree& e=tree[id]; 
    int le=e.le,ri=e.ri; 
    if(x<=le&&ri<=y) 
    { 
        e.lazy+=c; 
        pushup(id); 
        return; 
    } 
    int mid=(le+ri)/2; 
    if(x<=mid) update(id*2,x,y,c); 
    if(y>mid)  update(id*2+1,x,y,c); 
    pushup(id); 
    return; 
} 
LL solve() 
{ 
    LL ret=0; 
    sort(Nod+1,Nod+num+1); 
    sort(A+1,A+num+1); 
    int k=1; 
    for(int i=2;i<=num;i++) if(A[i]!=A[k]) A[++k]=A[i]; 
    build_tree(1,1,k); 
    for(int i=1;i<num;i++) 
    { 
        node& t=Nod[i]; 
        int x=lower_bound(A+1,A+k+1,t.L)-A; 
        int y=lower_bound(A+1,A+k+1,t.R)-A-1; 
        update(1,x,y,t.s); 
        ret+=(LL)tree[1].sum*((LL)Nod[i+1].H-Nod[i].H); 
    } 
    return ret; 
} 
int main() 
{ 
    while(scanf("%d",&N)!=EOF) 
    { 
        num=0; 
        int a,b,h; 
        for(int i=1;i<=N;i++) 
        { 
            scanf("%d%d%d",&a,&b,&h); 
            Nod[++num]=node(a,b,0,1); 
            A[num]=a; 
            Nod[++num]=node(a,b,h,-1); 
            A[num]=b; 
        } 
        LL ans=solve(); 
        printf("%I64d\n",ans); 
    } 
    return 0; 
}
View Code

poj3225(线段树区间异或)

/*
题意:给出了几种集合运算,每次操作对一段区间进行其中的一种运算
输出最后被覆盖的区间段,要注意开区间与闭区间

解析:注意一下每种运算如何修改区间的值,在线段树里再增加两个变量
d(整个区间的值),c(是否需要异或),具体细节见代码。
*/
#include<cstdio> 
#include<cstring> 
#include<string> 
#include<cmath> 
#include<iostream> 
#include<algorithm> 
#include<vector> 
#include<set> 
#include<map> 
#include<stack> 
#include<queue> 
#include<sstream> 
#include<utility> 
#include<iterator> 
using namespace std; 
const int INF=1e9+7; 
const double eps=1e-7; 
typedef __int64 LL; 
#define fa tree[id] 
#define lson tree[id*2] 
#define rson tree[id*2+1] 
#define e tree[id] 
const int maxn=140000; 
struct Tree 
{ 
    int le,ri,d,c; //d是区间的值,c代表是否需要异或
    void UpXor() 
    { 
        if(d!=-1) d^=1; //是整个连续相同的
        else c^=1; //懒惰标记异或
    } 
}tree[4*maxn]; 
void build_tree(int id,int le,int ri) 
{ 
    e.le=le,e.ri=ri,e.d=0,e.c=0; 
    if(le==ri) return; 
    int mid=(le+ri)/2; 
    build_tree(id*2,le,mid); 
    build_tree(id*2+1,mid+1,ri); 
    return; 
} 
inline void pushdown(int id) 
{ 
    if(fa.d!=-1) 
    { 
        lson.d=rson.d=fa.d; 
        lson.c=rson.c=0; 
        fa.d=-1; 
    } 
    if(fa.c) 
    { 
       lson.UpXor(); 
       rson.UpXor(); 
       fa.c=0; 
    } 
} 
inline void Link(int id,char op) 
{ 
    if(op=='U') fa.d=1,fa.c=0;  //并集
    else if(op=='D') fa.d=0,fa.c=0; //差集S-T
    else if(op=='C'||op=='S') fa.UpXor(); //异或
} 
void update(int id,int x,int y,char op) 
{ 
    int le=e.le,ri=e.ri; 
    if(x<=le&&ri<=y) { Link(id,op); return; } 
    pushdown(id); 
    int mid=(le+ri)/2; 
    if(x<=mid) update(id*2,x,y,op); //左边可更新
    else if(op=='I'||op=='C') lson.d=lson.c=0;  //如果是交集或者是差集T-S,左边都要置为0
    if(y>mid) update(id*2+1,x,y,op);  //右边同理
    else if(op=='I'||op=='C') rson.d=rson.c=0; 
    return; 
} 
int mark[maxn]; //标记某位置是否被占
vector<pair<int,int> > ve; 
void query(int id) 
{ 
    if(e.le==e.ri) { mark[e.le]=e.d; return; } //一直压到叶子节点
    pushdown(id); 
    query(id*2); 
    query(id*2+1); 
    return; 
} 
int main() 
{ 
    char op,a,b; 
    int x,y; 
    build_tree(1,0,131070); //扩大了2倍
    while(scanf("%c %c%d,%d%c",&op,&a,&x,&y,&b)!=EOF) 
    { 
        getchar(); 
        x*=2; y*=2; //乘2
        if(a=='(') x++;  //不是实区间加1 
        if(b==')') y--; 
        if(x>y) //区间为空
        { 
            if(op=='I'||op=='C') tree[1].c=tree[1].d=0; 
            continue; 
        } 
        update(1,x,y,op); //更新 
    } 
    memset(mark,0,sizeof(mark)); 
    query(1); //整个压下去
    ve.clear(); 
    int pre=-1; 
    for(int i=0;i<=131075;i++) 
    { 
        if(mark[i]) 
        { 
            if(pre!=-1) continue; 
            pre=i; 
        } 
        else
        { 
            if(pre==-1) continue; 
            ve.push_back(make_pair(pre,i-1)); //找区间段
            pre=-1; 
        } 
    } 
    if(ve.size()==0) printf("empty set\n"); //为空
    else
    { 
        int kase=0; 
        for(int i=0;i<ve.size();i++) //输出很恶心
        { 
            int L=ve[i].first; 
            int R=ve[i].second; 
            if(kase++) printf(" "); 
            if(L%2) printf("("); 
            else printf("["); 
            printf("%d,%d",L/2,(R+1)/2); 
            if(R%2) printf(")"); 
            else printf("]"); 
        } 
        printf("\n"); 
    } 
    return 0; 
}
View Code

codeforces 145E(树状数组)

给你n个数,实现两种操作,一种操作是把一段区间的数增加某个值,另一个操作是查询一段区间内的数满足"幸运数"要求的总个数,可以用树状数组实现.
因为题目已保证一个数的值不会超过10000,所以我们可以先把1~10000的幸运数找出来,或者直接打一个表,这样查询一个数是否是幸运数的时间复杂度就是O(1)
 
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
 
int a[100001],c[100002],kk[10002]={0};
int maxn;
 
inline int lowbit(int a)
{
    return a&(-a);
}
 
void add(int x,int num)
{
    while(x<=maxn)
    {
        c[x]+=num;
        x+=lowbit(x);
    }
}
int getsum(int x)
{
    int sum=0;
    while(x)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
bool jug(int a)
{
    while(a)
    {
        if(a%10!=4&&a%10!=7) return 0;
        a/=10;
    }
    return 1;
}
int main()
{
    int i,ans,n,m,b,a1,c1,x1,x2;
    char s[10];
    for(i=1;i<=10001;i++)
        if(jug(i)) kk[i]=1;
    while(~scanf("%d %d",&n,&m))
    {
        maxn=n;
        for(i=0; i<=n+1; i++)
            c[i]=0;
        for( i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            if(kk[a[i]]==1) add(i,1);
        }
        while(m--)
        {
            scanf("%s",s);
            if(s[0]=='c')
            {
                scanf("%d %d",&a1,&b);
                printf("%d\n",getsum(b)-getsum(a1-1));
            }
            else
            {
                scanf("%d %d %d",&a1,&b,&c1);
                for(i=a1; i<=b; i++)
                {
                    x1=kk[a[i]];
                    a[i]+=c1;
                    x2=kk[a[i]];
                    if(x1==1&&x2==0) add(i,-1);
                    else if(x1==0&&x2==1) add(i,1);
                }
            }
        }
    }
    return 0;
}
View Code

Codeforces276 E(在树上的操作)

题意:

给你一个无向图  除了 1号节点以外每个节点的 度都是 2   即 一个入度一个出度

给你一些操作

输入0vxd 表示在距离v节点d内的所有节点都增加x

输入1v   表示查询v节点的值

关键就在于第一个操作的时候往上的节点会延伸到其他树链上去    这真尼玛麻烦

博客上给的思路和我一致  维护两种树  一个是在原树链上的操作    一个是操作延伸出来的

敲完代码A掉之后我的内心真的久久不能平静

但是还是有一个地方不大懂   若有大神看到还望指点一下

 

Code

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cmath>

#include <algorithm>

#include <queue>

#include <vector>

 

using namespace std;

const int maxn=100005;

int n,qnum,v,x,d,dis[maxn],root[maxn];//dis表示树的深度,root表示树的首节点,以这两个数组定位一个节点

vector<int> g[maxn];//实现无向图

vector<vector<long long> > tree;//树的实现

 

int lowbit(int x)

{

    return x&(-x);

}

 

void add(int st,int en,int val,int cur)

{

    if(st>en) return;

    int sz=tree[cur].size();

    for(int i=st;i<sz;i+=lowbit(i))

        tree[cur][i]+=val;

    for(int i=en+1;i<sz;i+=lowbit(i))//鶸表示这里不是很理解…………为什么不能一个st~en循环呢…………

        tree[cur][i]-=val;

}

 

long long getsum(int dep,int cur)

{

    long long ret=0;

    int sz=tree[cur].size();

    for (int i=dep; i>0; i-=lowbit(i))

        ret+=tree[cur][i];

    return ret;

}

 

void dfs(int u,int pre,int dep,int cur)//dfs初始化

{

    dis[u]=dep;

    root[u]=cur;

    tree[cur].push_back(0);//每次push进一个0使得size增加,同时初始化每棵树

    int sz=g[u].size();

    for (int i=0; i<sz; i++)

    {

        int v=g[u][i];

        if (v!=pre)

            dfs(v,u,dep+1,cur);

    }

}

 

int main()

{

    while(scanf("%d%d",&n,&qnum)!=EOF)

    {

        int a,b;

        for(int i=0; i<=n; i++) g[i].clear();

        for(int i=0; i<n-1; i++)

        {

            scanf("%d%d",&a,&b);

            g[a].push_back(b);

            g[b].push_back(a);

        }

        int sz=g[1].size();

        tree.resize(sz+5);

        for(int i=0; i<sz; i++)

        {

            tree[i+1].push_back(0);

            dfs(g[1][i],1,1,i+1);

        }

        tree[0].resize(maxn,0);//对tree[0]初始化

        int op;

        long long sum=0;

        while(qnum--)

        {

            scanf("%d",&op);

            if(op)

            {

                scanf("%d",&v);

                int cur=root[v];

                if(v==1)

                    printf("%I64d\n",sum);

                else

                    printf("%I64d\n",getsum(dis[v],cur)+getsum(dis[v],0));//答案是由tree[0]与其原树相加结果

            }

            else

            {

                scanf("%d%d%d",&v,&x,&d);

                int cur=root[v];

                if(d<dis[v]) add(dis[v]-d,dis[v]+d,x,cur);//在一条树链上足以操作

                else

                {

                    sum+=x;//记录所有add   便于v==1时输出

                    add(1,dis[v]+d,x,cur);//在原树链上操作

                    add(1,d-dis[v],x,0);//上界操作由tree[0]负责

                    add(1,d-dis[v],-x,cur);//对原树链重复操作还原

                }

            }

        }

    }

    return 0;

}
View Code

hdu5316(线段树区间合并)

看一句:A beautiful subsequence is a subsequence that all the adjacent pairs of elves in the sequence have a different parity of position。
意思是说定义美丽子序列是区间里面的一个子序列,这个子序列中任意相邻的两个数在原序列中的具有不同的奇偶性
两种操作,1.把序列x位置的值变成y;2.询问区间[x,y]里面的最大美丽子序列(即序列和最大)
区间合并的时候要满足当左子的美丽子序列如果是以奇位置结尾,那么右子的美丽子序列要以偶开头
若左边以偶结尾,右边则以奇开头
那么只需要保存区间内“偶始偶终”、“偶始奇终”、“奇始偶终”、“奇始奇终”四种美丽子序列,合并的时候奇偶配对着合并
用一个二维数组s[2][2]表示起点和终点的奇偶性,其中0表示偶,1表示奇
 
还有,其中最大的美丽子序列不可以是空集(意思是说,当最大美丽子序列为负的时候不要取0 。。。。)
1.#include<iostream> 

2.#include<cstdio> 

3.#include<algorithm> 

4.using namespace std; 

5.typedef long long ll; 

6.const int N = 100000; 

7.const ll inf = 1LL<<30LL; 

8.struct Node 

9.{ 

10.    int l,r; 

11.    ll s[2][2];//0偶1奇 

12.}q[4*N]; 

13.#define ls i<<1 

14.#define rs i<<1|1 

15.void pushup(int i) 

16.{ 

17.    for (int j = 0;j <= 1;++j) 

18.    { 

19.        for (int k = 0;k <= 1;++k) 

20.        { 

21.            q[i].s[j][k] = max(q[ls].s[j][k],q[rs].s[j][k]); 

22.            q[i].s[j][k] = max(max(q[ls].s[j][0]+q[rs].s[1][k],q[ls].s[j][1]+q[rs].s[0][k]),q[i].s[j][k]); 

23.        } 

24.    } 

25.} 

26.void build(int i,int l,int r) 

27.{ 

28.    q[i].l = l,q[i].r = r; 

29.    if (l == r) 

30.    { 

31.        ll y; 

32.        scanf("%I64d",&y); 

33.        for (int j = 0;j <= 1;++j) 

34.        { 

35.            for (int k = 0;k <= 1;++k) 

36.            { 

37.                q[i].s[j][k] = -inf; 

38.            } 

39.        } 

40.        if (l&1) //奇数 

41.        { 

42.            q[i].s[1][1] = y; 

43.        } 

44.        else q[i].s[0][0] = y; 

45.        return ; 

46.    } 

47.    int mid = (l+r)>>1; 

48.    build(ls,l,mid); 

49.    build(rs,mid+1,r); 

50.    pushup(i); 

51.} 

52.void update(int i,int x,ll y) 

53.{ 

54.    if (x == q[i].l &&x == q[i].r) 

55.    { 

56.        if (x&1) //奇数 

57.        { 

58.            q[i].s[1][1] = y; 

59.        } 

60.        else q[i].s[0][0] = y; 

61.        return; 

62.    } 

63.    if (x <= q[ls].r) update(ls,x,y); 

64.    else update(rs,x,y); 

65.    pushup(i); 

66.} 

67.Node query(int i,int l,int r) 

68.{ 

69.    if (l<=q[i].l&&q[i].r<=r) return q[i]; 

70.    int mid = (q[i].l+q[i].r)>>1; 

71.    if (r <= mid) return query(ls,l,r); 

72.    else if (l > mid) return query(rs,l,r); 

73.    else 

74.    { 

75.        Node ql = query(ls,l,mid); 

76.        Node qr = query(rs,mid+1,r); 

77.        Node ans; 

78.        for (int j = 0;j <= 1;++j) 

79.        { 

80.            for (int k = 0;k <= 1;++k) 

81.            { 

82.                ans.s[j][k] = max(ql.s[j][k],qr.s[j][k]); 

83.                ans.s[j][k] = max(max(ql.s[j][0]+qr.s[1][k],ql.s[j][1]+qr.s[0][k]),ans.s[j][k]); 

84.            } 

85.        } 

86.        return ans; 

87.    } 

88.} 

89.int main() 

90.{ 

91.    int T;scanf("%d",&T); 

92.    while (T--) 

93.    { 

94.        int n,m;scanf("%d %d",&n,&m); 

95.        build(1,1,n); 

96.        int op;ll a,b; 

97.        for (int i = 0;i < m;++i) 

98.        { 

99.            scanf("%d",&op); 

100.            scanf("%I64d %I64d",&a,&b); 

101.            if (op == 0) 

102.            { 

103.                Node ans = query(1,(int)a,(int)b); 

104.                ll mx = - inf; 

105.                for (int j = 0;j <= 1;++j) 

106.                { 

107.                    for (int k = 0;k <= 1;++k) 

108.                    { 

109.                        mx = max(ans.s[j][k],mx); 

110.                    } 

111.                } 

112.                printf("%I64d\n",mx); 

113.            } 

114.            else if (op == 1) 

115.            { 

116.                update(1,(int)a,(ll)b); 

117.            } 

118.        } 

119.    } 

120.    return 0; 

121.}  
View Code

poj3486(线段树区间更新)

区间更新和区间查询有点类似

 

区间更新是由上往下的

每到一个点 判断这个点所代表的区间是不是在要更改的区间内 

是的话在这个点更改这个点的数值标记 并且把这个点的值在原来值的基础增减(这个点区间范围*更改值 )并且返回 (不一定到最底层)

如果这个点本身的区间和他的子区间都不满足目标区间的范围 直接返回

否则 pushdown  再进入子区间进行判断  

要注意返回后的路径上的值的更新

 

因为是自上而下的 所以一直到更新完目标区间 根节点到目标区间路径上点都被更新了

查询的时候 也是把查询路径上的点一路pushdown 所以最后的答案也是更新过的

1.#include<iostream>  

2.#include<algorithm>  

3.#include<cstdlib>  

4.#include<cctype>  

5.#include<cstdio>  

6.#include<string>  

7.#include<cstring>  

8.#include<vector>  

9.#include<set>  

10.#include<map>  

11.#include<queue>  

12.#include<cmath>  

13.#define pi acos(-1.0)  

14.#define inf 1<<29  

15.#define INF 0x3f3f3f3f  

16.#define zero 1e-8  

17.  

18.const int li[] = { -1, 0, 1, 0};  

19.const int lj[] = {0, -1, 0, 1};  

20.  

21.const int N = 1e5 + 10;  

22.  

23.using namespace std;  

24.  

25.struct node {  

26.  

27.    long long data;  

28.    long long tag;  

29.  

30.} tree[N * 4];  

31.  

32.int arr[N];  

33.  

34.void build(int node, int Begin, int End)  

35.{  

36.    tree[node].tag = 0;  

37.    if (Begin == End) {  

38.        tree[node].data = arr[Begin];  

39.        return;  

40.    }  

41.    build(node * 2, Begin, (Begin + End) / 2);  

42.    build(node * 2 + 1, (Begin + End) / 2 + 1, End);  

43.    tree[node].data = tree[node * 2].data + tree[node * 2 + 1].data;  

44.}  

45.long long fin;  

46.  

47.void pushdown(int node, int l, int r)  

48.{  

49.  

50.    tree[node * 2].tag += tree[node].tag;  

51.    tree[node * 2].data += tree[node].tag * ((l + r) / 2 - l + 1);  

52.    tree[node * 2 + 1].tag += tree[node].tag;  

53.    tree[node * 2 + 1].data += tree[node].tag * (r - (l + r) / 2);  

54.    tree[node].tag = 0;  

55.}  

56.  

57.void update(int node, int b, int e, int l, int r, long long data)  

58.{  

59.  

60.    if (l > e || r < b) return;  

61.  

62.    if (l >= b && r <= e) {  

63.        tree[node].tag += data;  

64.        tree[node].data += data * (r - l + 1);  

65.        return;  

66.    }  

67.  

68.    pushdown(node, l, r);  

69.    update(node * 2, b, e, l, (l + r) / 2, data);  

70.    update(node * 2 + 1, b, e, (l + r) / 2 + 1, r, data);  

71.  

72.    tree[node].data = tree[node * 2].data + tree[node * 2 + 1].data;  

73.}  

74.  

75.  

76.void query(int node, int b, int e, int l, int r)  

77.{  

78.  

79.    if (l > e || r < b) return;  

80.  

81.    if (l >= b && r <= e) {  

82.        fin += tree[node].data;  

83.        return;  

84.    }  

85.    pushdown(node, l, r);  

86.    query(node * 2, b, e, l, (l + r) / 2);  

87.    query(node * 2 + 1, b, e, (l + r) / 2 + 1, r);  

88.}  

89.int main()  

90.{  

91.  

92.    int n, q;  

93.    scanf("%d %d", &n, &q);  

94.  

95.    for (int i = 1; i <= n; ++i)  

96.        scanf("%d", &arr[i]);  

97.  

98.    build(1, 1, n);  

99.  

100.    for (int i = 0; i < q; ++i) {  

101.  

102.        char ch[10];  

103.        scanf("%s", ch);  

104.        if (ch[0] == 'C') {  

105.            int a, b, c;  

106.            scanf("%d%d%d", &a, &b, &c);  

107.            update(1, a, b, 1, n, c);  

108.        } else if (ch[0] == 'Q') {  

109.            int a, b;  

110.            scanf("%d%d", &a, &b);  

111.            fin = 0;  

112.            query(1, a, b, 1, n);  

113.            printf("%lld\n", fin);  

114.        }  

115.    }  

116.  

117.    return 0;  

118.}  
View Code

poj1177(扫描线求周长)

题意:

题目就是说n个矩形组成的新图形的周长,每个矩形会给出左下角和右上角的坐标。

分析:

       线段树+扫描线+离散化,我是从左到右扫描的,需要将纵坐标离散化,然后每扫描到一根线,求出其覆盖y方向的总长度,减去上次求得的长度,即为本条线增加或减少的长度,同时可以求出每两条线之间的距离,即为横坐标方向的周长的一部分,由于是矩形构成的,横坐标方向是为周长的部分一定成对出现,同时需要用到一个很重要的记录(num),因为可能出现两条竖线之间有4,6…甚至更多条外围的线段。

源代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
const int N=5005;
typedef __int64 ll;
 
struct seg
{
    int y1,y2;
    int x;
    int flag;
    seg(){}
    seg(int _y1,int _y2,int _x,int f):y1(_y1),y2(_y2),x(_x),flag(f){}
    friend bool operator < (seg a,seg b)
    {
        if(a.x==b.x)
            return a.flag>b.flag;
        return a.x<b.x;
    }
}Line[N<<1];
 
struct TNode
{
    int l,r;
    int num;//子树中有多少条线段
    int cover;//是否被覆盖,入边加1,出边-1
    int len;//当前节点覆盖y方向的总长度
    bool lb,rb;//左右节点是否被覆盖
}tre[N<<3];
vector<int>vec;
 
void build(int o,int l,int r)
{
    tre[o].l=l;
    tre[o].r=r;
    tre[o].num=0;
    if(l==r-1)
        return;
    int mid=(l+r)/2;
    build(o<<1,l,mid);
    build(o<<1|1,mid,r);
}
 
void update_len(int o)
{
    if(tre[o].cover>0)
    {
        tre[o].len=vec[tre[o].r]-vec[tre[o].l];
        tre[o].lb=tre[o].rb=true;
        tre[o].num=1;
        return;
    }
    if(tre[o].l==tre[o].r-1)
    {
        tre[o].cover=tre[o].lb=tre[o].rb=0;
        tre[o].num=tre[o].len=0;
        return;
    }
    tre[o].lb=tre[o<<1].lb;
    tre[o].rb=tre[o<<1|1].rb;
    tre[o].len=tre[o<<1].len+tre[o<<1|1].len;
    tre[o].num=tre[o<<1].num+tre[o<<1|1].num-(tre[o<<1].rb&tre[o<<1|1].lb);
}
 
void update(int o,seg p)
{
    if(p.y1<=vec[tre[o].l]&&p.y2>=vec[tre[o].r])
    {
        tre[o].cover+=p.flag;
        update_len(o);
        return;
    }
    if(tre[o].l==tre[o].r-1)
        return;
    int mid=(tre[o].l+tre[o].r)/2;
    if(p.y1<=vec[mid])
        update(o<<1,p);
    if(p.y2>vec[mid]) update(o<<1|1,p);
    update_len(o);
}
 
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int k=0;
        for(int i=0;i<n;i++)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            Line[k++]=seg(y1,y2,x1,1);////y[k]=y1;
            Line[k++]=seg(y1,y2,x2,-1);////y[k]=y2;
            vec.push_back(y1);
            vec.push_back(y2);
        }
 
        sort(vec.begin(),vec.end());
        vec.erase(unique(vec.begin(),vec.end()),vec.end());
        sort(Line,Line+k);
        build(1,0,vec.size()-1);
        //printf("1111\n");
        int ans=0;
        int old=0;
        int lines=0;
        for(int i=0;i<k;i++)
        {
            //printf("%d\n",Line[i].x);
            update(1,Line[i]);
            if(i>=1)
            {
                ans+=lines*2*(Line[i].x-Line[i-1].x);
            }
            ans+=abs(tre[1].len-old);
            old=tre[1].len;
            lines=tre[1].num;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

搜索

hdu3812(深搜+剪枝)

题意: 给出N对字符串,每对字符串可以相连,问能否找到一条从sea到sky的字符串链,每个字符串只能出现一次,如果能,输出
长度最长的,如果有多解,输出字典序最小的。无解输出what a pity
 
解析: 刚开始还以为是个有环找最长路的图论题(然而我并不会写)。。。。。。看了别人题解才知道是深搜+剪枝。
对字符串排序标号,如果没有出现sea或者sky,直接无解,然后并查集一下,如果不在同一个集合,同样无解,然后判断每个点
(起点和终点除外)是否能到达起点和终点,不能的在搜的过程中直接不管,然后就是搜了,搜的过程中更新解,如果最大长度
已经用完了所有的字符串就没必要再搜了(最开始我没管这个超时了。。。。。。后来加了这个剪枝就过了)

代码
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=202;
int N,id,be,en;
string S1[maxn],S[maxn];
int d[maxn];
int root(int a){ return d[a]==a?a:d[a]=root(d[a]); }
bool G[maxn][maxn];
int GetId(string& s) //找到下标
{
    for(int i=1;i<=id;i++) if(S[i]==s) return i;
    return 0;
}
bool input()
{
    N*=2;
    for(int i=1;i<=N;i++)
    {
        cin>>S1[i];
        S[i]=S1[i];
    }
    sort(S+1,S+N+1);  //排个序
    be=en=0;  //起点和终点编号
    id=1;
    for(int i=2;i<=N;i++)
    {
        if(S[i]!=S[id]) S[++id]=S[i];  //去重
        if(S[id]=="sea") be=id;
        if(S[id]=="sky") en=id;
    }
    if(!be||!en) return false; //没有sea或者sky
    for(int i=0;i<maxn;i++) d[i]=i;  //并查集
    memset(G,false,sizeof(G));
    for(int i=1;i<N;i+=2)
    {
        int a=GetId(S1[i]);  //下标
        int b=GetId(S1[i+1]);
        int ra=root(a);
        int rb=root(b);
        G[a][b]=G[b][a]=true;  //可连
        if(ra!=rb) d[rb]=ra; //合并
    }
    if(root(be)!=root(en)) return false; //不在同一集合
    return true;
}
bool vis[maxn],tvis[maxn];
int maxl,temp[maxn],ans[maxn];
bool dfs(int x,int step)
{
    vis[x]=true;
    temp[step]=x;
    if(x==en)  //到终点
    {
        if(step>maxl) //更新解
        {
            maxl=step;
            for(int i=0;i<=maxl;i++) ans[i]=temp[i];
        }
        if(step==id-1) return true; //用完所有的
    }
    for(int i=1;i<=id;i++)
        if(!vis[i]&&G[x][i])
    {
        if(dfs(i,step+1)) return true;
    }
    vis[x]=false;
    return false;
}
bool Reach(int x,int y)
{
    if(x==y) return true;
    for(int i=1;i<=id;i++)
        if(!tvis[i]&&!vis[i]&&G[x][i])
        {
            tvis[i]=true;
            if(Reach(i,y)) return true;
        }
    return false;
}
void solve()
{
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=id;i++)
    {
        memset(tvis,false,sizeof(tvis));
        tvis[be]=true;
        if(!Reach(i,en)) vis[i]=true;  //能否到终点
    }
    for(int i=1;i<=id;i++)
    {
        memset(tvis,false,sizeof(tvis));
        tvis[en]=true;
        if(!Reach(i,be)) vis[i]=true; //能否到起点
    }
    maxl=0;  //最大长度
    dfs(be,0); //深搜
}
int main()
{
    int T,Case=0;
    cin>>T;
    while(T--)
    {
        cin>>N;
        if(!input())  //无解
        {
            printf("Case %d: what a pity\n",++Case);
            continue;
        }
        solve();
        printf("Case %d:",++Case);
        for(int i=0;i<=maxl;i++) cout<<" "<<S[ans[i]];
        cout<<endl;
    }
    return 0;
}
View Code

hdu1401(双向bfs)

题意:棋盘大小为8*8,然后有4个球,给出初始所有球的位置以及目标位置,每次可以移动一个球,要么移动到旁边空的地方,要么跨过一个
球到空的地方(不能跨过多个球),问能否在8步以内到达目标状态。
 
解析:限定了8步,而且前后搜效果是一样的,所以可以考虑用双向bfs,然后8个数哈希(也可以开多维数组保存),要注意对4个点要排序。

代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int mod=500007;
const int maxn=200005;
int dx[]={-1,0,1,0},dy[]={0,-1,0,1};
bool in(int x,int y){ return x>=1&&x<=8&&y>=1&&y<=8; }
struct point   //球的坐标
{
    int x,y;
    point(int x=0,int y=0):x(x),y(y){}
    bool operator < (const point& t) const
    {
        if(x!=t.x) return x<t.x;
        return y<t.y;
    }
};
struct node
{
    point p[4];  //每个节点保存4个球的坐标,并且是排好序的
}nod[2][maxn];   //开二维用于双向搜
struct Hash
{
    int v,k,nid,next;   //保存哈希值,0或1,下标,next指针
}ha[mod+maxn];
int f[2],r[2],hash_id;  //队首队尾指针
bool Same(int k1,int a,int k2,int b)  //判断两个结构体是否完全相同
{
    for(int i=0;i<4;i++)
    {
        if(nod[k1][a].p[i].x!=nod[k2][b].p[i].x) return false;
        if(nod[k1][a].p[i].y!=nod[k2][b].p[i].y) return false;
    }
    return true;
}
int GetHash(point p[])  //得到哈希值
{
    int ret=0,k=1;
    for(int i=0;i<4;i++)
    {
        ret+=p[i].x*k; k*=3;  //k是系数
        ret+=p[i].y*k; k*=3;
    }
    return ret;
}
int Insert_Hash(int v,int k,int nid)  //插入
{
    int a=v%mod;
    int p=ha[a].next;
    while(p!=-1)
    {
        if(ha[p].v==v&&Same(ha[p].k,ha[p].nid,k,nid)) return ha[p].k==k?0:1;  //有相同的状态,k值相同返回0,不同返回1
        p=ha[p].next;
    }
    p=++hash_id;
    ha[p].v=v; ha[p].k=k; ha[p].nid=nid;  //增加新的节点
    ha[p].next=ha[a].next; ha[a].next=p;
    return -1;   
}
bool Has(node& t,int x,int y)
{
    for(int i=0;i<4;i++) if(t.p[i].x==x&&t.p[i].y==y) return true;  //此处是球
    return false;
}
bool AddNode(node& t,int i,int j,int k)
{
    int x=t.p[i].x;
    int y=t.p[i].y;
    int nx=x+dx[j];
    int ny=y+dy[j];
    if(!in(nx,ny)) return false;   //出界
    if(Has(t,nx,ny)) nx+=dx[j],ny+=dy[j]; //有球
    if(!in(nx,ny)) return false;  //出界
    if(Has(t,nx,ny)) return false; //还有球
    node& tt=nod[k][r[k]];
    tt=t;
    tt.p[i].x=nx; tt.p[i].y=ny;
    sort(tt.p,tt.p+4);   //排序
    int a=Insert_Hash(GetHash(tt.p),k,r[k]);
    if(a==1) return true;  //找到解
    else if(a==-1) r[k]++;  //增加新节点
    return false;  
}
bool bfs(int k)
{
    int en=r[k];
    while(f[k]<en)
    {
        node& t=nod[k][f[k]++];
        for(int i=0;i<4;i++)   //4个球4个方向
            for(int j=0;j<4;j++) if(AddNode(t,i,j,k)) return true;
    }
    return false;
}
bool solve()
{
    if(Same(0,0,1,0)) return true;  //相同
    int step=0;
    f[0]=f[1]=0; r[0]=r[1]=1;
    for(int i=0;i<mod;i++) ha[i].next=-1;
    hash_id=mod-1;
    Insert_Hash(GetHash(nod[0][0].p),0,0);
    Insert_Hash(GetHash(nod[1][0].p),1,0);
    while(f[0]<r[0]||f[1]<r[1])
    {
        if(step>=4) return false;
        step++;
        if(bfs(0)) return true;
        if(bfs(1)) return true;
    }
    return false;
}
int main()
{
    int x,y;
    while(scanf("%d%d",&x,&y)!=EOF)
    {
        nod[0][0].p[0]=point(x,y);
        for(int i=1;i<4;i++)
        {
             scanf("%d%d",&x,&y);
             nod[0][0].p[i]=point(x,y);
        }
        for(int i=0;i<4;i++)
        {
             scanf("%d%d",&x,&y);
             nod[1][0].p[i]=point(x,y);
        }
        sort(nod[0][0].p,nod[0][0].p+4);
        sort(nod[1][0].p,nod[1][0].p+4);
        if(solve()) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
View Code

模拟贪心乱搞yy

hdu3640(植物大战僵尸那题,模拟+二分)

题意:模拟植物大战僵尸,地图只有一行,有两种植物:豌豆射手(P)和炸弹土豆(M),僵尸要进攻,每只僵尸的hp是50,每个豌豆射手的hp是10,
僵尸如果走在土豆上,土豆会爆炸,所有在土豆处的僵尸hp都变为0,如果僵尸走在豌豆射手处,则每只僵尸对豌豆射手攻击一次,造成1点的伤害,
豌豆射手会攻击走在最前面的僵尸,每个豌豆射手攻击一次,造成1点的伤害。问最少需要多少只僵尸才能赢。
 
解析:很恶心的模拟,要注意的是僵尸不是一个接着一个排成队放在右边,可以几个僵尸放在同一个位置开始进攻,不然如果一开始就有超过50个
豌豆射手,那岂不是进来一个死一个(我最开始以为是一个一个放,以为不会有这种无解的情况。。。。。。),如果僵尸碰到的是炸弹,那么下一
回合可以认为僵尸的最左位置为炸弹的左边。如果碰到的不是炸弹,则不停向左统计植物个数,直到遇到第一个炸弹,然后二分可以吃掉这些植物
的最少僵尸个数。详见代码实现。

代码
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<utility>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<iterator>
#include<stack>
using namespace std;
const int maxn=105;
char S[maxn];
int L,Pnum,Mnum,step;
void init()
{
    L=strlen(S)-1;
    Pnum=Mnum=0;  //豌豆射手与土豆的个数
    for(int i=0;i<=L;i++)
        if(S[i]=='P') Pnum++;
        else Mnum++;
}
bool judge(int totpnum,int pnum,int znum)
{
    int nowstep=step;
    int P_hp=10,Z_hp=50;  //豌豆射手和僵尸的hp
    while(pnum>0&&znum>0)
    {
        if(nowstep>0){ nowstep--; Z_hp-=totpnum; } //没走到豌豆射手处
        else{ P_hp-=znum; Z_hp-=totpnum; } //走到了两个都要受伤害
        if(P_hp<=0)  //死一个射手
        {
            P_hp=10;
            totpnum--; pnum--;
            nowstep=1;
        }
        if(Z_hp<=0){ Z_hp=50; znum--; } //死一个僵尸
    }
    if(pnum<=0&&znum>0) return true; //所有豌豆射手挂掉而僵尸有多的
    return false;
}
int BIS(int totpnum,int pnum) //二分
{
    int x=1,y=2*maxn,mid;
    while(x<=y)
    {
        mid=(x+y)/2;
        if(judge(totpnum,pnum,mid)) y=mid-1;
        else x=mid+1;
    }
    return y+1;
}
int solve()
{
    int ret=0;  //ret是需要的僵尸的个数
    if(S[L]=='M') { ret++; L--; step=2; } //最右边是土豆,step代表走到下一个位置需要的步数
    else step=1;
    while(L>=0)  //知道小于0才算赢
    {
        if(S[L]=='M') //是土豆
        {
            if(step>1) //往前走
            {
                step--;
                if(Pnum>=50) ret++; //超过50个豌豆射手死一个僵尸
                continue;
            }
            else ret++;  //炸死一个
            Mnum--; L--;
            step=2; //step变为2
        }
        else  //如果是豌豆射手
        {
            int pnum=0;
            for(int i=L;i>=0;i--)  //一直到找到土豆
                if(S[i]=='M') break;
                else pnum++;
            ret+=BIS(Pnum,pnum);  //二分得到最小的僵尸个数
            Pnum-=pnum;
            L-=pnum+1;  //多减1表示把土豆算进去了,因为有没死的僵尸在土豆处牺牲
            step=2;
        }
    }
    if(S[0]=='M') ret++; //如果最左边是土豆,则还需要一个僵尸去攻击首脑
    return ret;
}
int main()
{
    int T,Case=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",S); //植物
        init();
        printf("Case %d: %d\n",++Case,solve());
    }
    return 0;
}
View Code

hdu3866(贪心)

题意:n人合伙买一件价格为p的礼物,每个人有自己能承担的最大费用,要保证尽可能公平,使付钱多的人和付钱少的人钱数的差距尽可能小,如果存在矛盾,先来的人比后来的付钱多。

分析:要使尽可能公平,可以先将总价格平分,如果有人能承担的最大费用比平均价格要少,则要支付自己全部的钱,然后剩余的钱由其它人循环按照此法解决,当最终剩余的人能承担的最大费用都比当前平均费用多时,则按照题目规则需多付钱的人比其他人少付1cent即可完全解决问题。可见该题是一道贪心问题,可创建结点数组a用于保存每人能承担的最大费用s和自己的序号x,用数组b按序号保存没人需要支付的费用。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=10005;
int p,n;
bool vis[maxn]; //标记数组,若某结点已访问,则将其按序号标记为true
struct node
{
    int s,x;
}a[maxn];
int b[maxn];

bool cmp(const node &a,const node &b) //将结点数组按s从小到大排序,若s相同则按x从小到大排序
{
    if(a.s<b.s) return true;
    else if(a.s==b.s) return a.x>b.x;
    return false;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        memset(vis,false,sizeof vis);
        scanf("%d%d",&p,&n);
        ll sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i].s);
            a[i].x=i;
            sum+=a[i].s;
        }
        if(sum<p)
        {
            puts("IMPOSSIBLE");
            continue;
        }
        int nn=n;
        while(p)
        {
            sort(a,a+n,cmp);
            int ave=p/nn,mod=p%nn;
            int num=0;
            for(int i=0;i<n;i++)
            {
                if(vis[a[i].x]) continue;
                if(a[i].s<=ave)
                {
                    b[a[i].x]=a[i].s;
                    p-=a[i].s;
                    vis[a[i].x]=true;
                    num++;
                    nn--;
                }
                else break;
            }
            if(num==0)
            {
                    for(int i=n-1;i>=0;i--)
                    {
                        if(vis[a[i].x]) continue;
                        if(mod)
                        {
                            b[a[i].x]=ave+1;
                            mod--;
                        }
                        else b[a[i].x]=ave;
                    }
                break;
            }
        }
        for(int i=0;i<n-1;i++)
            printf("%d ",b[i]);
        printf("%d\n",b[n-1]);
    }
    return 0;
}
 
View Code

codeforces 239 div2 D

基本题意:总共有n+1个房间,每个房间有两个门,在这里我假设为A门和B门,这两个门其中第一个只能回到前边几个房间,假设该房间为第i个房间,那么他只能回到对应的前边的1到i号房间(ps这里为什么会有i号房间呢,后边一句来解释),具体是多少,得看输入的时候对应的是多少(也就是说如果你输入的就是i,那么也就是代表又再次回到原来的房间,回到同一个房间),第二道门是只能够到达下边一个房间,假设现在就在第i个房间,那么只能前往第i+1号房间,我在这里假设这两个房间为A门和 B门,A门往前边走,B门往后边走。而且一走进一个房间之后 ,这个房间就会有个计数器,这个计数器是只要你进去就会记一次数,如果计数之后为奇数(odd number),那么就只能走A门出去,即回到前边几个房间 
数学公式的考察:这道题主要注意有个循环的过程,每次从一个门里边出来(如果你再次回到同一个房间,那么这个房间的计数器会变成奇数,(因为一开始是从这个房间的而A门出去的(ps因为你是通过回到同一个房间的,也就是说你是从A门走到1到i号房间的才到这歌儿同一个房间),那么也就是说那个时候计数器就是奇数,那么当你一进来那么就变成偶数了)这个时候就会走B门,到达那个i+1号房间)这就得出一个结论,就是只要你第一次走进一个房间(这个房间计数器变为奇数1),那么你一定是先走A门出去,前往前边几个房间(包括自己的房间),然后就再回来这个房间(这个房间计数器变成2(偶数)),然后就走B们出去走到i+1号房间最后要求你到达i+1号房间总共花多少步骤。上边说的都是理解题意得思路; 
下边来说解题思路:记s[i]为在第i号房间从B门出来前往i+1号门的时候所已经用过的步子数。所求的s[i]就是从i号房间的A们出去再回到这个房间所需的步子数加上回到这个房间之后从B门出去前往i+1号房间的步子数(当然这里包括了到达第i-1号房间已经走过的步子数)。最后求得就是s[n];s[i]=2*s[i-1]-s[a[i]-1]+2;这个就是递推关系式,下边简单解释一下,这个柿子变下心就是s[i]=(s[i-1]+1)+(s[i-1]-s[a[i]-1]+1),前边一个就是从第i-1号房间直接走到i号房间,然后直接走B门到i+1号房间,中间没有算A号门,即没有考虑从i号门回到前边的第a[i]号门,你理解一下下,后边一个柿子就是从第i号门通过走A号门回到第a[i]号门,然后再回来的所花的步子数,在这里有个小技巧,从第i号房间回到第a[i]号房间其实就等价于从a[i]-1号房间到达a[i]号房间的,也就是s[a[i]-1],这里读者好好细想一下,应该可以想明白的。 
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream> 
using namespace std; 
int n; 
int a[1005]; 
long long s[1005]; 
int main() 
{ 
while(~scanf("%d",&n)) 
{ 
for(int i=1; i<=n; i++) 
scanf("%d",&a[i]); 
memset(s,0,sizeof s); 
s[0]=0; 
for(int i=1;i<=n;i++) 
s[i]=(2*s[i-1]%1000000007-s[a[i]-1]%1000000007+1000000009)%1000000007; 
printf("%I64d\n",s[n]%1000000007); 
} 
return 0; 
}
View Code

hdu1112(模拟)

模拟 注意钥匙的左右边缘不能超过锁的范围 就算是空白也不行

1.#include<iostream>  

2.#include<algorithm>  

3.#include<cstdlib>  

4.#include<cctype>  

5.#include<cstdio>  

6.#include<string>  

7.#include<cstring>  

8.#include<vector>  

9.#include<queue>  

10.#include<cmath>  

11.#define pi acos(-1.0)  

12.#define inf 1<<29  

13.#define INF 0x3f3f3f3f  

14.#define zero 1e-8  

15.  

16.using namespace std;  

17.  

18.char lock[10507][1207];  

19.char key[127][127];  

20.bool flag[1107][1207];  

21.  

22.struct edge {  

23.    int x, y;  

24.} lef[10007], rig[10007], down[10007];  

25.  

26.int l, rr, d;  

27.int r, c, n, m;  

28.int depth;  

29.int len;  

30.void todo()  

31.{  

32.    l = 0;  

33.    r = 0;  

34.    d = 0;  

35.    len = 0;  

36.  

37.    for (int i = 0; i < rr; ++i)  

38.        for (int j = 0; j < c; ++j) {  

39.            if (key[i][j] == '#') {  

40.                if (j > 0 && key[i][j - 1] == '#') continue;  

41.                lef[l].x = i;  

42.                lef[l++].y = j;  

43.  

44.            }  

45.        }  

46.    for (int i = 0; i < rr; ++i)  

47.        for (int j = c - 1; j >= 0; --j) {  

48.            if (key[i][j] == '#') {  

49.                if (j < c - 1 && key[i][j + 1] == '#') continue;  

50.                rig[r].x = i;  

51.                rig[r++].y = j;  

52.            }  

53.        }  

54.  

55.    for (int j = 0; j < c; ++j)  

56.        for (int i = rr - 1; i > -1; --i) {  

57.            if (key[i][j] == '#') {  

58.                if (i < rr - 1 && key[i + 1][j] == '#') continue;  

59.                down[d].x = i;  

60.                down[d++].y = j;  

61.            }  

62.        }  

63.  

64.}  

65.  

66.bool j_left(int x, int y)  

67.{  

68.    for (int i = 0; i < l; ++i)  

69.        if (y < 0 || lock[lef[i].x + x][lef[i].y + y] == '#') {  

70.            return false;  

71.        }  

72.  

73.    return true;  

74.}  

75.  

76.bool j_right(int x, int y)  

77.{  

78.  

79.    for (int i = 0; i < r; ++i) {  

80.        if (c + y > m || lock[rig[i].x + x][rig[i].y + y] == '#') {  

81.            return false;  

82.        }  

83.  

84.    }  

85.  

86.    return true;  

87.}  

88.  

89.bool j_down(int x, int y)  

90.{  

91.  

92.  

93.    for (int i = 0; i < d; ++i) {  

94.  

95.        if (x > n + rr || lock[down[i].x + x][down[i].y + y] == '#') {  

96.            return false;  

97.        }  

98.    }  

99.    return true;  

100.}  

101.  

102.void rem(int x, int y)  

103.{  

104.  

105.    flag[down[0].x + x][down[0].y + y] = true;  

106.}  

107.  

108.bool ifdo(int x, int y)  

109.{  

110.  

111.    if (!flag[down[0].x + x][down[0].y + y]) return true;  

112.    return false;  

113.  

114.}  

115.void dfs(int x, int y)  

116.{  

117.  

118.    if (x > depth) depth = x;  

119.    if (depth == n + rr) return ;  

120.    rem(x, y);  

121.  

122.    if (j_down(x + 1, y) && ifdo(x + 1, y)) dfs(x + 1, y);  

123.    if (j_left(x, y - 1) && ifdo(x, y - 1)) dfs(x, y - 1);  

124.    if (j_right(x, y + 1) && ifdo(x, y + 1)) dfs(x, y + 1);  

125.  

126.}  

127.  

128.  

129.int main()  

130.{  

131.  

132.    int t;  

133.    cin >> t;  

134.    for (; t--;) {  

135.  

136.        scanf("%d %d", &rr, &c);  

137.  

138.        for (int i = 0; i < rr; ++i) {  

139.            scanf("%s", key[i]);  

140.        }  

141.  

142.        scanf("%d %d", &n, &m);  

143.  

144.        memset(lock, '.', sizeof(lock));  

145.  

146.        for (int i = rr; i < rr + n; ++i) {  

147.            scanf("%s", lock[i]);  

148.        }  

149.  

150.  

151.        todo();  

152.        depth = 0;  

153.        if (c > m) {  

154.            printf("The key falls to depth 0.\n");  

155.            continue;  

156.        }  

157.        if (r == 0 && l == 0 && d == 0) {  

158.            printf("The key can fall through.\n");  

159.            continue;  

160.        }  

161.  

162.        memset(flag, false, sizeof(flag));  

163.  

164.        dfs(0, 0);  

165.        if (depth >= n + rr) printf("The key can fall through.\n");  

166.        else  

167.            printf("The key falls to depth %d.\n", depth);  

168.    }  

169.  

170.    return 0;  

171.}  
View Code

HDU5301

题意:
给axb大小的矩阵求出 不覆盖X点的能填满这个矩阵的 最大的矩阵的最小值
 
因为没有给n m谁大谁小, 先处理了,保证n是短边,m是长边.这样方便计算.(如果要改变的时候,记得将x,y也交换)
先不管X点,计算均分这个矩形的最小矩形的长度,就是最短边/2往上取整的那个数.
有特殊情况 如果 m==n && n是奇数 && x==y && x==(n+1)/2 这种情况 

n是偶数的话 不会出现这样.
正常的话 是这样   (例 n=7 m=19    x=1 y=5)
n!=m 时 就判断 x  y的位置  来判断这个矩形的长度
而且 因为要最小 所以短边一直为1

 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
using namespace std;
int main()
{
    int n,m,x,y;
    while (~scanf("%d%d%d%d",&n,&m,&x,&y))
    {
        if (n>m)
        {
            swap(n,m);
            swap(x,y);
        }
        int ans=n/2;
        int half=n-ans;
        ans=max(ans,half);
        if (n==m && n%2 && x==y && x==(n+1)/2)
            ans--;
        else if (n!=m)
        {
            int h=max(x-1,n-x);
            int w=min(y-1,m-y)+1;
            if (w>ans)
                ans=max(ans,min(h,w));
        }
        printf("%d\n",ans);
    }
    return 0;
}
 
View Code

图论

hdu1384(差分约束)

题意:对于每个区间[a,b]至少要有c个元素,问集合里元素的最小个数。
 
解析:差分约束,用Ti表示区间[0,i-1]有多少个元素在里面,则满足下面的条件
  Tb+1-Ta>=c
  0<=Ti+1-Ti<=1
  则建边(a,b+1,c),(i,i+1,0),(i+1,i,-1) 然后用spfa求得答案。注意这题用vector可能会超时。

代码
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<utility>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<iterator>
#include<stack>
using namespace std;
const int INF=1e9+7;
const double eps=1e-7;
const int maxn=50005;
int N;
struct edge
{
    int u,v,w,next;
    edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){next=-1; }
}E[maxn*3];
int head[maxn],dist[maxn];
bool inq[maxn];
queue<int> que;
int spfa(int be,int en)
{
    for(int i=be;i<=en;i++) dist[i]=-INF;
    dist[be]=0;
    memset(inq,false,sizeof(inq));
    while(!que.empty()) que.pop();
    que.push(be);
    while(!que.empty())
    {
        int u=que.front();  que.pop();
        inq[u]=false;
        for(int i=head[u];i!=-1;i=E[i].next)
        {
            int v=E[i].v,w=E[i].w;
            if(dist[v]<dist[u]+w)  //更新
            {
                dist[v]=dist[u]+w;
                if(!inq[v]){ inq[v]=true; que.push(v); }
            }
        }
    }
    return dist[en];
}
int main()
{
    while(scanf("%d",&N)!=EOF)
    {
        memset(head,-1,sizeof(head));
        int u,v,w,cnt=0;
        int minv=INF,maxv=-INF;
        for(int i=0;i<N;i++)
        {
            scanf("%d%d%d",&u,&v,&w);  //建边(u,v+1,w);
            v++;
            E[++cnt]=edge(u,v,w);
            E[cnt].next=head[u];
            head[u]=cnt;
            minv=min(minv,u);
            maxv=max(maxv,v);
        }
        for(int i=minv;i<maxv;i++)
        {
            E[++cnt]=edge(i,i+1,0); //建边(i,i+1,0)
            E[cnt].next=head[i];
            head[i]=cnt;
            E[++cnt]=edge(i+1,i,-1); //建边(i+1,i,-1)
            E[cnt].next=head[i+1];
            head[i+1]=cnt;
        }
        printf("%d\n",spfa(minv,maxv));
    }
    return 0;
}
View Code

codeforces 14D

题意:

给你一个无向不成环的图    让你找出两条不相交的树链  使其乘积最大

 

思路  枚举每一条道路   并使其暂时断裂   再从这条道路的两端开始搜索  寻找最长的树链即可

 

说实话 搜索的题目虽然说做的还凑合   但是这种给你一个连通方式  并以此形成无向图    再加以搜索的题目我还是第一次做

 

我先想得是用二维矩阵保存路径   以G[u][v]  表示从  u到v是连通的      再用vector省点空间

之后我较为习惯的用了BFS    然后就进入了无限懵逼崩溃懵逼崩溃的状态      说到底用矩阵保存如果不用上结构体的话    很难记录链长

无奈还是写了DFS

 

#include <iostream>

#include <cstdio>

#include <vector>

#include <algorithm>

#include <cmath>

#include <cstring>

#include <queue>

 

using namespace std;

const int maxn=220;

bool vis[maxn];

vector<int> G[maxn];

int ans;

 

int dfs(int v,int x)

{

    int sum=0;

    int max1=0,max2=0;

    for(int i=0; i<G[v].size(); i++)

    {

        if(G[v][i]!=x)

        {

            sum=max(sum,dfs(G[v][i],v));//sum得到从一个NODE出发的树的直径   循环操作得到最大

                                        //与此同时 ans也在计算着从改点出发的最长树链

            if(ans>max1)

            {

                max2=max1;

                max1=ans;

            }

            else max2=ans>max2?ans:max2;

        }

    }

    sum=max(sum,max1+max2);//最后sum得到从V出发的树的直径

    ans=max1+1;//max1为该node的最长树链  因为递归返回  最大树链长度+1

    return sum;

}

 

int main()

{

    int n,a,b;

    while(scanf("%d",&n)!=EOF)

    {

        for(int i=0; i<=n; i++) G[i].clear();

        for(int i=0; i<n-1; i++)

        {

            scanf("%d%d",&a,&b);

            G[a].push_back(b);

            G[b].push_back(a);

        }

        int last_ans=0;

        for(int i=0; i<n; i++)

        {

            int num=G[i].size(),temp;

            for(int j=0; j<num; j++)

            {

                int ans=0;

                temp=dfs(i,G[i][j])*dfs(G[i][j],i);

                last_ans=max(last_ans,temp);

            }

        }

        printf("%d\n",last_ans);

    }

    return 0;

}
View Code

hdu5294(最短路+最大流)

题意:

n个点,m条边,两个人,A在点1处,B在点n处,A必须走最短路到达n点(只走最短路,最短路可能多条)

B切断图中的某些边不让A到达n点

问:1.B最少切断多少边使A不能到达n点

       2.B最多切断多少边使A还能达到n点

分析:

问题1求最小割,根据最大流最小割定理,利用最大流求得,流量是1(边数)

对于问题2,只需在所有的最短路中找到边数最少的,然后用总边数m减去最少边的最短路上的边数
解法:

以给出的边权求最短路,根据最短路建图,判断边是否在最短路里面:d[v] - d[u] == w(u,v)

根据所建的新图,再求最短路(求最短路中的最少边数),每条边的权值变为1,

根据新图求最大流,流量为1(即最小割里的边权是1,是边数)

说明:代码很长不过都是模板,最大流的bfs、dfs模板都可以,最短路用的SPFA模板(很奇怪用Dijkstra怎么都过不了,不是超时而是WA。。。)

代码:dfs版增广路:

1.#define mem(a,x) memset(a,x,sizeof(a)) 

2.#include<iostream> 

3.#include<cstdio> 

4.#include<cstring> 

5.#include<algorithm> 

6.#include<queue> 

7.#include<set> 

8.#include<stack> 

9.#include<cmath> 

10.#include<map> 

11.#include<stdlib.h> 

12.#include<cctype> 

13.#include<string> 

14.using namespace std; 

15.typedef long long ll; 

16.const int N = 2000; 

17.const int inf = 1 << 27; 

18.const int M = 120000; 

19./****************************************最大流模板*********************************************/ 

20.struct Edge 

21.{ 

22.    int to;//终点 

23.    int cap;//容量 

24.    int rev;//反向边 

25.    Edge (int to = 0, int cap = 0, int rev = 0) : to (to), cap (cap), rev (rev) {} 

26.}; 

27.struct EdmondsKarp 

28.{ 

29.    bool used[M + 5]; 

30.    vector<Edge>v[M + 5]; 

31.    void AddEdge (int from, int to, int cap) 

32.    { 

33.        v[from].push_back (Edge (to, cap, v[to].size() ) ); 

34.        v[to].push_back (Edge (from, 0, v[from].size() - 1) ); 

35.    } 

36.    int dfs (int s, int t, int f) 

37.    { 

38.        if (s == t) return f; 

39.        used[s] = 1; 

40.        for (int i = 0; i < v[s].size(); ++i) 

41.        { 

42.            Edge &tmp = v[s][i];//引用 

43.            if (!used[tmp.to] && tmp.cap > 0) 

44.            { 

45.                int d = dfs (tmp.to, t, min (f, tmp.cap) ); 

46.                if (d > 0) 

47.                { 

48.                    tmp.cap -= d; 

49.                    v[tmp.to][tmp.rev].cap += d; 

50.                    return d; 

51.                } 

52.            } 

53.        } 

54.        return 0; 

55.    } 

56.    int MaxFlow (int s, int t) //求源点汇点的最大流 

57.    { 

58.        int flow = 0; 

59.        while (1) //一直循环直到找不到增广路 

60.        { 

61.            mem (used, 0); 

62.            int f = dfs (s, t, inf); 

63.            if (f == 0) return flow; 

64.            flow += f; 

65.        } 

66.    } 

67.} q; 

68./***************************************最短路模板*****************************************/ 

69.struct Node 

70.{ 

71.    int v, w; 

72.    Node (int v = 0,int w = 0):v(v),w(w){} 

73.}; 

74.struct Spfa 

75.{ 

76.    int d[N+5]; 

77.    bool vis[N+5]; 

78.    queue<int>q; 

79.    vector<Node>u[N+5]; 

80.    void init(int n) 

81.    { 

82.        while (!q.empty()) q.pop(); 

83.        for (int i = 0;i <= n;++i) u[i].clear(); 

84.        mem(vis,0); 

85.    } 

86.    void AddEdge(int uu,int vv,int ww) 

87.    { 

88.        u[uu].push_back(Node(vv,ww)); 

89.        u[vv].push_back(Node(uu,ww)); 

90.    } 

91.    int spfa(int s, int t,int n) 

92.    { 

93.        for (int i = 0; i <= n; ++i) d[i] = inf; 

94.        q.push (s); 

95.        vis[s] = 1; 

96.        d[s] = 0; 

97.        while (!q.empty() ) 

98.        { 

99.            int h = q.front(); 

100.            q.pop(); 

101.            vis[h] = 0;//spfa不同于bfs 

102.            for (int i = 0; i < u[h].size(); ++i) 

103.            { 

104.                int &v = u[h][i].v, &w = u[h][i].w; 

105.                if (d[v] > d[h] + w) 

106.                { 

107.                    d[v] = d[h] + w; 

108.                    if (!vis[v]) 

109.                    { 

110.                        q.push (v); 

111.                        vis[v] = 1; 

112.                    } 

113.                } 

114.            } 

115.        } 

116.        return d[t]; 

117.    } 

118.} d,g; 

119. 

120./****************************************以最短路建图*********************************/ 

121.void make_map (int n) 

122.{ 

123.    for (int i = 1; i <= n; ++i) 

124.    { 

125.        for (int j = 0;j < d.u[i].size();++j) 

126.        { 

127.            int v = d.u[i][j].v, w = d.u[i][j].w; 

128.            if (d.d[v] - d.d[i] == w) 

129.            { 

130.                q.AddEdge(i,v,1);//最大流,流量是1 

131.                g.AddEdge(i,v,1);//最短路,边权是1 

132.            } 

133.        } 

134.    } 

135.} 

136./************************************************************************************/ 

137.int main() 

138.{ 

139.    int n, m; 

140.    while (~scanf ("%d %d", &n, &m) ) 

141.    { 

142.        d.init (n); 

143.        g.init (n); 

144.        mem (q.v, 0); 

145.        mem (q.used,0); 

146.        for (int i = 0, u, v, w; i < m; ++i) 

147.        { 

148.            scanf ("%d %d %d", &u, &v, &w); 

149.            d.AddEdge (u, v, w); 

150.        } 

151.        int minway = d.spfa (1, n, n); 

152.        make_map (n); 

153.        int ans1 = q.MaxFlow (1, n); 

154.        int ans2 = m - g.spfa (1, n, n); 

155.        printf ("%d %d\n", ans1, ans2); 

156.    } 

157.    return 0; 

158.} 

159./* 

160. 

161.7 12 

162.1 2 1 

163.2 7 8 

164.1 7 8 

165.1 6 7 

166.6 7 1 

167.2 3 3 

168.3 6 3 

169.6 4 1 

170.7 4 2 

171.1 5 2 

172.5 4 4 

173.7 5 6 

174. 

175.*/ 
bfs版的增广路:

1.#define mem(a,x) memset(a,x,sizeof(a)) 

2.#include<iostream> 

3.#include<cstdio> 

4.#include<cstring> 

5.#include<algorithm> 

6.#include<queue> 

7.#include<set> 

8.#include<stack> 

9.#include<cmath> 

10.#include<map> 

11.#include<stdlib.h> 

12.#include<cctype> 

13.#include<string> 

14.using namespace std; 

15.typedef long long ll; 

16.const int N = 2000; 

17.const int inf = 1<<27; 

18.struct Node 

19.{ 

20.    int v, w; 

21.    Node (int v = 0,int w = 0):v(v),w(w){} 

22.}; 

23.struct Spfa 

24.{ 

25.    int d[N+5]; 

26.    bool vis[N+5]; 

27.    queue<int>q; 

28.    vector<Node>u[N+5]; 

29.    void init(int n) 

30.    { 

31.        while (!q.empty()) q.pop(); 

32.        for (int i = 0;i <= n;++i) u[i].clear(); 

33.        mem(vis,0); 

34.    } 

35.    void AddEdge(int uu,int vv,int ww) 

36.    { 

37.        u[uu].push_back(Node(vv,ww)); 

38.        u[vv].push_back(Node(uu,ww)); 

39.    } 

40.    int spfa(int s, int t,int n) 

41.    { 

42.        for (int i = 0; i <= n; ++i) d[i] = inf; 

43.        q.push (s); 

44.        vis[s] = 1; 

45.        d[s] = 0; 

46.        while (!q.empty() ) 

47.        { 

48.            int h = q.front(); 

49.            q.pop(); 

50.            vis[h] = 0;//spfa不同于bfs 

51.            for (int i = 0; i < u[h].size(); ++i) 

52.            { 

53.                int &v = u[h][i].v, &w = u[h][i].w; 

54.                if (d[v] > d[h] + w) 

55.                { 

56.                    d[v] = d[h] + w; 

57.                    if (!vis[v]) 

58.                    { 

59.                        q.push (v); 

60.                        vis[v] = 1; 

61.                    } 

62.                } 

63.            } 

64.        } 

65.        return d[t]; 

66.    } 

67.} d,g; 

68.struct Edge 

69.{ 

70.    int from, to, cap, flow; 

71.    Edge (int u = 0, int v = 0, int c = 0, int f = 0) : from (u), to (v), cap (c), flow (f) {} 

72.}; 

73.struct EdmondsKarp 

74.{ 

75.    int n, m; 

76.    vector<Edge>edges;//边数的两倍 

77.    vector<int>G[N+5];//邻接表,G[i][j]表示节点i的第j条边在e数组中的序号 

78.    int a[N+5];//当起点到i的可改进量 

79.    int p[N+5];//最短路树上p的入弧编号 

80.    void init (int n) 

81.    { 

82.        for (int i = 0; i <= n; ++i) G[i].clear(); 

83.        edges.clear(); 

84.    } 

85.    void AddEdge (int from, int to, int cap) 

86.    { 

87.        edges.push_back (Edge (from, to, cap, 0) ); 

88.        edges.push_back (Edge (to, from, 0, 0) ); //反向弧 

89.        m = edges.size(); 

90.        G[from].push_back (m - 2); 

91.        G[to].push_back (m - 1); 

92.    } 

93.    int MaxFlow (int s, int t) 

94.    { 

95.        int flow = 0; 

96.        while (1) 

97.        { 

98.            mem (a, 0); 

99.            queue<int>Q; 

100.            Q.push (s); 

101.            a[s] = inf; 

102.            while (!Q.empty() ) 

103.            { 

104.                int x = Q.front(); 

105.                Q.pop(); 

106.                for (int i = 0; i < G[x].size(); ++i) 

107.                { 

108.                    Edge & e = edges[G[x][i]]; 

109.                    if (!a[e.to] && e.cap > e.flow) 

110.                    { 

111.                        p[e.to] = G[x][i]; 

112.                        a[e.to] = min (a[x], e.cap - e.flow); 

113.                        Q.push (e.to); 

114.                    } 

115.                } 

116.                if (a[t]) break; 

117.            } 

118.            if (!a[t]) break; 

119.            for (int u = t; u != s; u = edges[p[u]].from) 

120.            { 

121.                edges[p[u]].flow += a[t]; 

122.                edges[p[u] ^ 1].flow -= a[t]; 

123.            } 

124.            flow += a[t]; 

125.        } 

126.        return flow; 

127.    } 

128.}q; 

129.void make_map (int n) 

130.{ 

131.    for (int i = 1; i <= n; ++i) 

132.    { 

133.        for (int j = 0;j < d.u[i].size();++j) 

134.        { 

135.            int v = d.u[i][j].v, w = d.u[i][j].w; 

136.            if (d.d[v] - d.d[i] == w) 

137.            { 

138.                q.AddEdge(i,v,1);//最大流,流量是1 

139.                g.AddEdge(i,v,1);//最短路,边权是1 

140.            } 

141.        } 

142.    } 

143.} 

144.int main() 

145.{ 

146.    int n, m; 

147.    while (~scanf ("%d %d", &n, &m) ) 

148.    { 

149.        d.init (n); 

150.        g.init (n);q.init(n); 

151.        for (int i = 0, u, v, w; i < m; ++i) 

152.        { 

153.            scanf ("%d %d %d", &u, &v, &w); 

154.            d.AddEdge (u, v, w); 

155.        } 

156.        int minway = d.spfa (1, n, n); 

157.        make_map (n); 

158.        int ans1 = q.MaxFlow (1, n); 

159.        int ans2 = m - g.spfa (1, n, n); 

160.        printf ("%d %d\n", ans1, ans2); 

161.    } 

162.    return 0; 

163.} 

164./* 

165. 

166.7 12 

167.1 2 1 

168.2 7 8 

169.1 7 8 

170.1 6 7 

171.6 7 1 

172.2 3 3 

173.3 6 3 

174.6 4 1 

175.7 4 2 

176.1 5 2 

177.5 4 4 

178.7 5 6 

179.*/ 
View Code

codeforces 14D

题意: 
本题目就是要求出一棵树上面两条路径长度的乘积,输出最大的乘积即可。 
思路: 
枚举每条边,对于每一条边,我都可以求出断开这条边以后得到的两条最长的路径的长度。其中的最大值就是答案。

代码

#include<cstdio>

#include<vector>

#include<cstring>

#include<algorithm>

using namespace std;

 

vector<int >ve[300];

int n;

int vis[300];

int dis[300];

int tar[300];

int zux,zuy;

 

bool ok(int x,int y)

{

 

    if(x==zux&&y==zuy)

        return 1;

    else if(x==zuy&&y==zux)

        return 1;

    return 0;

}

void dfs(int num)

{

    tar[num]=1;

    vis[num]=1;

    for(int i=0;i<ve[num].size();i++)

    {

        int x=ve[num][i];

        if(vis[x]||ok(x,num))

            continue;

        dis[x]=dis[num]+1;

        dfs(x);

    }

}

int pac(int x)

{

    memset(dis,0,sizeof dis);

    memset(vis,0,sizeof vis);

    dfs(x);

    int ans=0;

    for(int i=1;i<=n;i++)

    {

    if(dis[ans]<dis[i])

        ans=i;

    }

    memset(dis,0,sizeof dis);

    memset(vis,0,sizeof vis);

    dfs(ans);

    ans=0;

    for(int i=1;i<=n;i++)

    {

        if(ans<dis[i])

            ans=dis[i];

    }

    return ans;

}

int solve()

{

        memset(tar,0,sizeof tar);

        int t1=0,t2=0;

        if(ve[zux].size()==1)

            t1=0;

        else

        t1=pac(zux);

        if(ve[zuy].size()==1)

            t2=0;

        else

        t2=pac(zuy);

        return t1*t2;

}

int main()

{

    while(scanf("%d",&n)!=EOF)

    {

        for(int i=1;i<=n;i++)

            ve[i].clear();

        int x,y;

        for(int i=0;i<n-1;i++)

        {

            scanf("%d%d",&x,&y);

            ve[x].push_back(y);

            ve[y].push_back(x);

 

        }

        int ans=0;

        for(int i=1;i<=n;i++)

        {

            for(int j=0;j<ve[i].size();j++)

            {

                zux=i;

                zuy=ve[i][j];

                ans=max(ans,solve());

            }

           // printf("ans:%d\n",ans);

        }

        printf("%d\n",ans);

    }

 

    return 0;

}
View Code

数论

hdu2161

题意:求某个数的最大质因子是第几个质数
思路:用筛法打个带标记的质数表即可
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
const int MAXN = 1000000;
int str[MAXN + 10], prime[MAXN];
int Max(int a, int b)
{
    return a>b ? a : b;
}
void init()
{
    memset(str, 0, sizeof str);
    int cnt = 1;
    str[0] = 0;
    str[1] = 0;
    for (int i = 2; i <= MAXN; i++)
    {
        if (str[i] == 0)
        {
            str[i] = cnt;
            prime[cnt++] = i;
        }
        for (int j = 1; j < cnt && i * prime[j] <= MAXN; j++)
        {
            str[i * prime[j]] = Max(j, str[i]);
            if (i % prime[j] == 0)
            {
                break;
            }
        }
    }
}
int main()
{
    init();
    int n;
    while (~scanf("%d", &n))
    {
        printf("%d\n", str[n]);
    }
    return 0;
}
View Code

hdu3519(矩阵快速幂)

硬币所有可能排列情况为2^n,朝向相同硬币连续三个以下 的排列情况为a(1)=2,a(2)=4,a(3)=6,a(4)=10,观察或者推算得a(n)=a(n-1)+a(n-2);
那么连续三个或三个以上硬币的所有组合情况 为b(n)=2^n-a(n);
化为只含b(n)的递推式即 b(n)=b(n-1)+b(n-2)+2^(n-2);b(1)=0;b(2)=0;b(3)=2;b(4)=6;用矩阵快速幂可快速算得b(n);

代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
 
const int mod=10007;
struct node
{
    int a[4][4];
    node()
    {
        a[1][1]=a[1][2]=a[1][3]=a[2][1]=1;
        a[2][2]=a[2][3]=a[3][1]=a[3][2]=0;
        a[3][3]=2;
    }
    void init()           //将其初始化为单位矩阵
    {
        memset(a,0,sizeof(a));
        for(int i=1; i<4; i++)
            a[i][i]=1;
    }
};
 
node mul(node a,node b)  //(a*b)%mod  矩阵乘法
{
    node ans;
    for(int i=1; i<4; i++)
        for(int j=1; j<4; j++)
        {
            ans.a[i][j]=0;
            for(int k=1; k<4; k++)
                ans.a[i][j]+=a.a[i][k]*b.a[k][j];
            ans.a[i][j]%=mod;
        }
    return ans;
}
node power(node a,int n)    //(a^n)%mod  //矩阵快速幂
{
    node ans;
    ans.init();
    while(n)
    {
        if(n%2)//n&1
            ans=mul(ans,a);
        n/=2;
        a=mul(a,a);
    }
    return ans;
}
int main()
{
    int i,j,n;
    while(~scanf("%d",&n))
    {
        if(n<3){printf("0\n");continue;}
        node ans;
        ans=power(ans,n-2);
       
        printf("%d\n",(ans.a[1][3]*2)%mod);
    }
    return 0;
}
View Code

SGU 106(扩展欧几里得)

这题交的时候要注意是%I64d 所以我直接用了cin cout

/*

        方程两边同时除以gcd(a,b).我们假设aa=a/gcd(a,b),bb=b/gcd(a,b),nn=n/gcd(a,b)

        所以方程两边同时除以gcd(a,b)后,

        可以得到一个方程aa*x+bb*y=nn.

        并且该方程aa*x+bb*y=nn的解x,y就是a*x+b*y=n的解

        我们只要求解出aa*x+bb*y=1的其中一个解,设这两个解为x0,y0.

        那么aa*x+bb*y=nn的其中一个解解就是x0*nn,y0*nn.

        接着,a*x+b*y=n的其中一个解解也就是x0*nn,y0*nn.

        a*(x0*nn)+b*(y0*nn)=n. 

        a*(x0*nn+1*b)+b*(y0*nn-1*a)=n

        a*(x0*nn-1*b)+b*(y0*nn+1*a)=n.

        继续扩展

        a*(x0*nn+k*b)+b*(y0*nn-k*a)=n (k属于整数)

        nn=n/gcd(a,b).

        x=x0*nn+k*b

        y=y0*nn-k*a

 */

#include<iostream>

#include<cstdio>

#include<cstring>

#include<cmath>

#include<string>

#include<algorithm>

#include<queue>

#include<stack>

#include<set>

#include<map>

#include<vector>

using namespace std;

long long gcd(long long a,long long b)

{

    return b==0?a:gcd(b,a%b);

}

long long exgcd(long long a,long long b,long long &x,long long &y)

{

    if (!b)

    {

        x=1;

        y=0;

        return a;

    }

    long long gcd=exgcd(b,a%b,x,y);

    long long temp=x;

    x=y;

    y=temp-a/b*y;

    return gcd;

}

long long upper(long long a,long long b)//往上取整

{

    if (a<=0)

        return a/b;

    return (a-1)/b+1;

}

long long lower(long long a,long long b)//往下取整

{

    if (a>=0)

        return a/b;

    return (a+1)/b-1;

}

long long get(long long l,long long r,long long d,long long &k1,long long &k2)

{

    if (d<0)

    {

        l=-l;

        r=-r;

        d=-d;

        swap(l,r);

    }

    k1=upper(l,d);

    k2=lower(r,d);

}

int main()

{

    long long a,b,c,x1,y1,x2,y2;

    while (cin>>a>>b>>c)//ax+by+c=0 >>ax+by=-c

    {

        bool flag=0;

        c=-c;

        cin>>x1>>x2>>y1>>y2;

        long long ans,x,y;

        if (!a && !b && !c)

        {

            cout<<(x2-x1+1)*(y2-y1+1)<<endl;

            continue;

        }

        else if (!a && !b)

        {

            cout<<0<<endl;

            continue;

        }

        else if (!a)

        {

            if (c%b || c/b<y1 || c/b>y2)

                ans=0;

            else

                ans=x2-x1+1;

            cout<<ans<<endl;

            continue;

        }

        else if (!b)

        {

            if (c%a || c/a<x1 || c/a>x2)

                ans=0;

            else

                ans=y2-y1+1;

            cout<<ans<<endl;

            continue;

        }

        /*

        先处理了a,b,c为0的情况

        */

 

        long long g=gcd(a,b);

        if (c%g)//如果 c不是gcd(a,b)的倍数 无解

        {

            cout<<0<<endl;

            continue;

        }

        a/=g;

        b/=g;

        c/=g;// c=c/g 上面已经将c变为-c

        exgcd(a,b,x,y);

        long long k1,k2,k3,k4;

        x*=c;

        y*=c;

        get(x1-x,x2-x,b,k1,k2);

        get(y1-y,y2-y,-a,k3,k4);

//        cout<<x<<" "<<y<<endl;

        ans=min(k2,k4)-max(k1,k3)+1;

        cout<<ans<<endl;

    }

    return 0;

}
View Code

codeforces 300E

题意:

给定n个数(a1,a2……an),求p,其中p=n!,且p可以被(a1!*a2!*…….*an!)整除。

分析:

将a1!*a2!*……*an!分解成质因子相乘的形式,但是肯定不可能一个数一个数分解,巧妙的运用从大数转换成小数的思想,求出各个质因子及其个数。再运用二分求解对于每个质因子而言最小的nn,所有的nn取最大即为结果

源代码:

#include<cstdio>

#include<cstring>

#include<string>

#include<queue>

#include<map>

#include<cmath>

#include<stack>

#include<vector>

#include<set>

#include<iostream>

#include<algorithm>

using namespace std;

const int N=1000005;

typedef __int64 LL;

 

LL num[N*10];

int p[N*10];

bool vis[N*10];

int prime[N];

int tot=0;

LL sum;

int ma;

 

void init()

{

    memset(vis,false,sizeof vis);

    for(int i=2; i<N*10; i++)

    {

        if(!vis[i])

        {

            prime[tot++]=i;

            p[i]=i;

        }

        for(int j=0; j<tot&&i<=N*10/prime[j]; j++)

        {

            vis[i*prime[j]]=true;

            p[i*prime[j]]=prime[j];

            if(i%prime[j]==0)

                break;

        }

    }

}

 

void cal()

{

    for(int i=ma;i>=2;i--)

    {

        if(p[i]!=i)

        {

            num[p[i]]+=num[i];

            num[i/p[i]]+=num[i];

        }

    }

}

 

bool ok(LL m,int a,LL n)

{

    LL res=0;

    while(m)

    {

        res+=m/a;

        m/=a;

        if(res>=n)

            return true;

    }

    return false;

 

}

 

LL BST(int a,LL n)

{

    LL l=1,r=sum;

    LL ans=-1;

    while(l<=r)

    {

 

        LL mid=(l+r)/2;

        if(ok(mid,a,n))

        {

            ans=mid;

            r=mid-1;

        }

        else l=mid+1;

    }

    return ans;

}

 

LL solve()

{

    LL ans=1;

    for(int i=0;i<tot;i++)

    {

        if(num[prime[i]])

        {

            ans=max(ans,BST(prime[i],num[prime[i]]));

        }

        else break;

    }

    return ans;

}

 

int main()

{

    int n;

    init();

    while(~scanf("%d",&n))

    {

        memset(num,0,sizeof num);

        ma=0;

        sum=0;

        for(int i=0; i<n; i++)

        {

            int x;

            scanf("%d",&x);

            ma=max(ma,x);

            num[x]++;

            sum+=x;

        }

        for(int i=ma-1;i>=2;i--)

        {

            num[i]+=num[i+1];

        }

 

        cal();

 

        printf("%I64d\n",solve());

    }

    return 0;

}
View Code

Codeforces 696C(概率+组合数学+指数循环节)

题意:有三个杯子倒扣于桌面,放的位置编号1,2,3,刚开始2号地方的杯子里有一个key,现在每次操作为从1、3号位置上等概率选择一个与2号位置的杯子交换(换杯子,位置编号还是不变,可认为左、中、右三个,比如3换到1就是右边的杯子换到中间),求n次操作后key在2号位子的杯子里的概率,结果用分数。
题解:每次都是从1、3中选一个,显然概率就是1/2,而且每次2号位置的杯子都会变化。那么dp[i][n]表示第n次操作后,key在i号位置的杯子中的概率。
显然三个杯子,i=1,2,3;把换成一维的:f1(n),f2(n),f3(n).然后递推:
对于f1(n):可能第n-1次后key就在1位置,那么第n次只能选3号,即1/2*f1(n-1);可能第n-1次后key在2号中,那么只能选1位置,即1/2*f2(n-1);只有这两个可能到达,所以f1(n)=1/2*(f1(n-1)+f2(n-1));
对于f3(n),位置是对称的:f3(n)=f1(n);
类似1,f2(n)=1/2*(f1(n-1)+f3(n-1));
我们要求的是f2(n)
上面三个等式:f2(n)=1/2*(f2(n-1)+f2(n-2));
这个式子用组合数学求解线性常系数递推式(母函数内容)
最后f2(n)= .
题目对取模要求比较变态,必须是分子分母分别取模前要求化为最简分式,最简后再分别取模。上面的式子问题在于分母的3,因为2^(n-1)与分子已经没有公因子可以消了。那么看分子里面有没有3这个因子可以消:
很巧的是:当n-1为奇数时,2^(n-1)%3=2,此时n为偶数,(-1)^n=1;所以(2^(n-1)+(-1)^n)%3=0,同理n-1位偶数时有同样的结论
也就是说分子必然能消掉3;既然能整除,那么就把(2^(n-1)+(-1)^n)/3整体当做分子,而且是可以用逆元去处理3的,这样处理后就是最简分式了
即 ,inv是逆元,分子分母都是对1e9+7取模
现在问题就是n很大(不知道这里能用java搞),幂很大就是用指数循环节:
  
要求各欧拉函数
注意定理使用的条件,n较小时直接快速幂;
#pragma comment(linker, "/STACK:10240000,10240000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
const double R=0.5772156649015328606065120900;
const int N=1e5+5;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
    int n;
ll a[N],ini,M;
ll judge()
{
    ll tm=mod+10;
    ll ans=1;
    for(int i=0;i<n;i++)
    {
        if(ans>tm/a[i]) return 0;//很大
        ans*=a[i];
    }
    return ans;
}
ll Pow(ll a,ll n)
{
    ll ans=1;
    while(n)
    {
        if(n&1)
            ans=ans*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ans;
}
void solve(ll mo,ll tag)
{
    ll ans=1;
    for(int i=0;i<n;i++)
    {
        a[i]%=mo;//注意爆数据
        ans=ans*a[i]%mo;
    }
    ans--;
    ans=(ans%mo+mo)%mo;
    if(ans==0) ans+=mo;
    ll down=Pow(2,ans);
    ll up=down+tag;
    up=up*ini%mod;
    cout<<up<<'/'<<down<<endl;
}
int main()
{
     ini=333333336,M=1000000006;//事先求好逆元和欧拉值
    while(cin>>n)
    {
        int tag=-1;
        for(int i=0;i<n;i++)
        {
            scanf("%I64d",&a[i]);
            if(a[i]%2==0) tag=1;//
        }
        ll all=judge();
        if(all==0) {solve(M,tag);continue;}//幂很大
        if(all==1)
        {
            cout<<"0/1"<<endl;
            continue;
        }
        ll down=Pow(2,all-1);
        ll up=(down+tag)*ini%mod;
        cout<<up<<'/'<<down<<endl;

    }
    return 0;
}
View Code

Hdu 5728 PowMod(欧拉函数+指数循环节)

题意:只是那个k是无限个
题解:首先是算k:先了解两条性质,欧拉函数是积性函数;
(1)积性函数性质:F(m1*m2)=F(m1)*F(m2),当且近当gcd(m1,m2)=1时成立;
(2) ,其中p是n的素因子,且gcd(p,n/p)=1。这个用欧拉函数的素因子表达式很好证明。
有了这个再来算k,题目的n是很特殊的,它的每个素因子的幂次都是1:
那么假设素数p是n的一个素因子,显然gcd(p,n/p)=1;关键是i,如果i中没有素因子p,那么就直接用积性性质。如果i%p==0,必然可以写成i=k*p;即倍数关系,否则i%p!=0;
所以分成两部分求:
 
 .....p是素数,素数的欧拉值就是p-1;
 
 
到这里前两和式是可以合并的,考虑和式的上下限,含义不同,第二项的i表示的p的倍数i*p才是第一项i的含义,相当于第二项刚好把第一项补齐了,那么从1到m没有遗漏,而且第二项的i用第一项替换后里面也是n/p;最终
 
这是个e二元递归式:n/p和m/p看成整体,那么设原先求的为f(n,m),所以f(n,m)=(p的欧拉值)*f(n/p,m)+f(n,m/p);
每次枚举一个就够了,n每次要除以p,最多就是把它的每个素因子除完。
第二部分k的超级幂:用欧拉的定理:指数循环节
  
每次往幂次上一层模就取一次欧拉值,只有1的欧拉值等一自己,其他数的欧拉值都是小于自己的,所以模会不断变小至1,显然对1取模结果就是0,所以无限就变得有限了
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<utility>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<iterator>
#include<stack>
using namespace std;
const int INF=1e9+7;
const double eps=1e-7;
const int N=1e7+5;
const int M=1000000007;
typedef long long ll;
int pri[N],phi[N];
bool vis[N];
int tot;
ll sum[N];
void init()
{
    int n=N;
    tot=0;
    memset(vis,false,sizeof vis);
    phi[1]=1;
    for(int i=2;i<n;i++)
    {
        if(!vis[i])
        {
            pri[tot++]=i;
            phi[i]=i-1;
        }
        for(int j=0;j<tot && i*pri[j]<n;j++)
        {
            vis[i*pri[j]]=true;
            if(i%pri[j]==0)
            {
                phi[i*pri[j]]=phi[i]*pri[j];
                break;
            }
            else phi[i*pri[j]]=phi[i]*(pri[j]-1);
        }
    }
    sum[0]=0;
    for(int i=1;i<N;i++)
        sum[i]=(sum[i-1]+phi[i])%M;
}
ll Pow(ll a,ll n,ll mod)
{
    ll ans=1;
    while(n)
    {
        if(n&1)
        {
            ans=ans*a%mod;
//            if(ans>=mod)ans=ans%mod;
        }
        a=a*a%mod;
//        if(a>=mod) a=a%mod;
        n>>=1;
    }
    if(ans==0) ans+=mod;
    return ans;
}
ll solve(ll k,ll mod)
{
    if(mod==1) return mod;
    ll tmp=phi[mod];
    ll up=solve(k,tmp);
    ll ans=Pow(k,up,mod);
    return ans;
}
int rear;
int a[15];
void resolve(ll n)
{
    for(int i=0;i<tot;i++)
    {
        if(!vis[n]) {a[rear++]=n;break;}
        if(n%pri[i]==0)
        {
            a[rear++]=pri[i];
            n/=pri[i];
        }
    }
}
ll f(int pos,ll n,ll m)
{
    //pos即每个素数,一次一个就行了
    if(n==1) return sum[m];//n为1结果就是欧拉值的前缀和
    if(m==0)return 0;
    return ((a[pos]-1)*f(pos-1,n/a[pos],m)%M+f(pos,n,m/a[pos]))%M;
}
int main()
{
    init();//打表
    ll n,m,p;
    while(~scanf("%I64d%I64d%I64d",&n,&m,&p))
    {
        rear=0;
        resolve(n);//素因子分解
        ll k=f(rear-1,n,m);//算k
        ll ans=solve(k,p);
        printf("%I64d\n",ans%p);
    }
    return 0;
}
View Code

dp

HDU2845

题意:求在一个矩阵中能吃掉数字的最大和,当吃掉某个数字的时候,该数字的上一行(若是第0行则不处理)和下一行(若是最后一行不处理)和左右两个数字(同理)会消失
思路:先按吃一个则左右消失的规则处理出每一行能吃到的最大数字和(可以用DP),然后再按照吃一行上下两行消失的规则处理行的和
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
const int MAXN = 200010;
int n, m,str[MAXN];
int Max(int a,int b)
{
        return a>b?a:b;
}
int main()
{
        while (~scanf("%d%d", &n, &m))
        {
                int a[MAXN], b[MAXN];
                a[0] = b[0] = 0;
                for (int i = 1; i <= n; i++)
                {
                        int temp;
                        for (int j = 1; j <= m; j++)
                        {
                                scanf("%d", &temp);
                                a[j]=Max(a[j-1],b[j-1]);
                                b[j]=a[j-1]+temp;
                        }
                        str[i]=Max(a[m],b[m]);
                }
                a[0]=b[0];
                for(int i=1;i<=n;i++)
                {
                        a[i]=Max(a[i-1],b[i-1]);
                        b[i]=a[i-1]+str[i];
                }
                printf("%d\n",Max(a[n],b[n]));
        }
        return 0;
}
View Code

po2677(双调欧几里得旅行商问题)

题意:
给出n个点,要求从最左端点严格向右走到最右端点,再从最右端点走回来
途中必须经过所有点(保证每个点横坐标不同),求最短路。
解题思路:
双调欧几里得旅行商问题,dp。将点按横坐标从小到大排序,dp[i][j]表示从j向左走到1,再从1走到i的最短路(i<j)(此过程中1-j所有的点都被经历过)。
给出转移方程:
dp[i][j]=dp[i][j-1]+dist(j-1,j)    (i<=j-2);
dp[j-1][j]=min(dp[j-1][j],dp[i][j-1]+dist(i,j))   (i<=j-2);
按此方式更新不会丢失最优解(每次只把j纳入集合而不考虑j+1,j+2...)
最终得到dp[n][n]=dp[n-1][n]+dist(n-1,n);
代码:
1.    #include<iostream>  
2.    #include<cstring>  
3.    #include<cmath>  
4.    #include<iomanip>  
5.    using namespace std;  
6.    const double INF=1.0*9999999;  
7.    struct Node  
8.    {  
9.            int x,y;  
10.    };  
11.    Node a[1005];  
12.    double dp[1005][1005];  
13.    int n;  
14.    double dist(int i,int j)  
15.    {  
16.            return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));  
17.    }  
18.    int main()  
19.    {  
20.            while(~scanf("%d",&n))  
21.            {  
22.                    for(int i=1;i<=n;i++)  
23.                            scanf("%d%d",&a[i].x,&a[i].y);  
24.                    memset(dp,INF,sizeof dp);  
25.                    dp[1][2]=dist(1,2);  
26.                    for(int j=3;j<=n;j++)  
27.                    {  
28.                            for(int i=1;i<=j-2;i++)  
29.                                    dp[i][j]=dp[i][j-1]+dist(j-1,j);  
30.                            dp[j-1][j]=INF;  
31.                            for(int i=1;i<=j-2;i++)  
32.                                    dp[j-1][j]=min(dp[i][j-1]+dist(i,j),dp[j-1][j]);  
33.                    }  
34.                    dp[n][n]=dp[n-1][n]+dist(n-1,n);  
35.                    cout<<setprecision(2)<<fixed<<dp[n][n]<<endl;  
36.            }  
37.    }  
View Code

hdu2059

题意:
额,中文题不翻译。
解题思路:
看别人的博客才理解的,由于每个加油站都可以选择加油和不加油,为了把所有情况考虑进去,不妨令dp[i]为从起点到第i个加油站所需要的最短时间,那么dp[i]就应该是从0到i-1这些节点充满电然后直接跑到i的最短时间。为什么这样做不会丢失最优解?不妨考虑第4个节点,计算过程中你求出了dp[2]+t2和dp[3]+t3,这就等于说你已经考虑了第3个节点充电或者不充电的情况,
而且此时的dp[2]是它所能取得最小值,同理,dp[1]+t1和dp[2]+t2就表示考虑了第2个节点充电或者不充电的情况,那么有没有考虑第2个节点不充电但是第三个节点充电的情况呢?这里看上去是没有,但是你在计算dp[3]的时候,就已经考虑好了第二个点究竟该不该充电,所以第2个节点不充电但是第三个节点充电的情况可能会包含于dp[3]+t3中,好绕口啊。。
1.    #include<cstdio>  
2.    #include<cstring>  
3.    #include<string>  
4.    #include<iostream>  
5.    #include<sstream>  
6.    #include<algorithm>  
7.    #include<utility>  
8.    #include<vector>  
9.    #include<set>  
10.    #include<map>  
11.    #include<queue>  
12.    #include<cmath>  
13.    #include<iterator>  
14.    #include<stack>  
15.    using namespace std;  
16.    const double INF=9999999;  
17.    const double eps=1e-7;  
18.    const int mod=1000007;  
19.    const int maxn=50000;  
20.    double dp[maxn];  
21.    int a[maxn];  
22.    int main()  
23.    {  
24.            int l,n,c,t,vr,v1,v2;  
25.            while(cin>>l)  
26.            {  
27.                    cin>>n>>c>>t;  
28.                    cin>>vr>>v1>>v2;  
29.                    for(int i=1;i<=n;i++)  
30.                            cin>>a[i];  
31.                    memset(dp,INF,sizeof dp);  
32.                    sort(a+1,a+1+n);  
33.                    a[0]=0;  
34.                    a[n+1]=l;  
35.                    dp[0]=0;  
36.                    for(int i=1;i<=n+1;i++)  
37.                    {  
38.                            for(int j=0;j<i;j++)  
39.                            {  
40.                                    double tt;  
41.                                    if(c>a[i]-a[j])  
42.                                            tt=1.0*(a[i]-a[j])/v1;  
43.                                    else  
44.                                            tt=1.0*c/v1+1.0*(a[i]-a[j]-c)/v2;  
45.                                    dp[i]=min(dp[i],dp[j]+tt);  
46.                            }  
47.                            dp[i]+=t;//默认充满电  
48.                    }  
49.      
50.                    if(dp[n+1]-t>1.0*l/vr)  
51.                            cout<<"Good job,rabbit!"<<endl;  
52.                    if(dp[n+1]-t<1.0*l/vr)  
53.                            cout<<"What a pity rabbit!"<<endl;  
54.            }  
55.    }  
View Code

hdu5076(dp+状态压缩)

题意:
旅行商问题,给出n个位置,求从起点(0,0)出发遍历所有位置再回到原点得最短路。

解题思路:
1.DP+状态压缩,以二进制保存当前遍历状态,如果此状态下某个点未被经过,则用此状态转移到经过该店的状态,更新其值。
2.注意到n很小,DFS搜一遍~。

代码1:
1.    #include <iostream>  
2.    #include <cstdio>  
3.    #include <cstring>  
4.    #include <algorithm>  
5.    #include <cmath>  
6.    using namespace std;  
7.    const int N=55;  
8.    const int INF=0x3f3f3f3f;  
9.    int dp[(1<<11)+15][N];  
10.    int dist[N][N];  
11.    struct Node  
12.    {  
13.        int x,y;  
14.    }p[N];  
15.    int main()  
16.    {  
17.        int n,m,t;  
18.        int cnt;  
19.        while(cin>>n>>m)  
20.        {  
21.      
22.            cnt=0;  
23.            for(int i=0; i<n; i++)  
24.                for(int j=0; j<n; j++)  
25.                {  
26.                    cin>>t;  
27.                    if(t||(i==0&&j==0))  
28.                    {  
29.                        p[cnt].x=i;  
30.                        p[cnt++].y=j;  
31.                    }  
32.                }  
33.            for(int i=0; i<cnt; i++)  
34.            {  
35.                for(int j=i+1; j<cnt; j++)  
36.                    dist[i][j]=dist[j][i]=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y);  
37.                    dist[i][i]=0;  
38.            }  
39.            memset(dp,INF,sizeof(dp));  
40.            dp[0][0]=0;  
41.            for(int i=0; i<(1<<cnt); i++)  
42.            {  
43.                for(int j=0; j<cnt; j++)  
44.                {  
45.                    if(dp[i][j]==INF)  
46.                            continue;  
47.                    for(int k=0; k<cnt; k++)  
48.                    {  
49.                        if(i&(1<<k))  
50.                            continue;  
51.                        dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dist[j][k]);  
52.                    }  
53.                }  
54.            }  
55.            cout<<dp[(1<<cnt)-1][0]<<endl;  
56.        }  
57.    }  
View Code

uva10118(分析+状压)

题意:桌上有4堆糖果,每堆糖果高度不超过40,每颗糖果有一种颜色(一共20种,1,2,3...,20),

有一个篮子,一开始是空的,每当里面装有两颗颜色相同的糖果时,就可以从篮子里拿出这一对糖果。

如果篮子里的糖果数量为5个并且不能再拿时,篮子充满,游戏结束。问最多能拿走多少对糖果。

糖果堆上的所有糖果拿光了也算结束

 

样例:

5

1 2 3 4

1 5 6 7

2 3 3 3

4 9 8 6

8 7 2 1

解释:

每堆糖果高度为5,

从左往右为4堆糖果,数字代表颜色。堆顶在上。

结果:

8

 

一开始看到这个题会觉得状态根本就无法表示,因为你需要保存每一堆的剩余层数,还需要保存篮子里的糖果数量和颜色,即使状态存下了,转移起来时间复杂度也太高了。

41^4* 21^5实在太大。

后来想想根本不用保存这么多状态。因为保存了每堆剩余的层数就知道了拿掉的糖果,又因为篮子里同种颜色的糖果只能是0或者1(同色糖会被拿掉),所以可以推出篮子里糖果的状态。

将糖果编号改为0到19,然后用状压表示每个状态(每堆剩余层数)对应篮子里的糖果。

 

 

 

#include<cstdio>

#include<string>

#include<cstring>

#include<iostream>

#include<cmath>

#include<algorithm>

#include<vector>

using namespace std;

 

#define all(x) (x).begin(), (x).end()

#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)

#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)

#define ysk(x)  (1<<(x))

typedef long long ll;

typedef pair<int, int> pii;

const int INF =0x3f3f3f3f;

const int maxn=40    ;

int a[5][maxn+3];

int state[maxn+10][maxn+10][maxn+10][maxn+10];

int dp[maxn+3][maxn+3][maxn+3][maxn+3];

int n;

 

int getNum(int &s)//计算篮子s里的糖果数量

{

    int cnt=0;

    for(int i=0;i<20;i++) if(s&ysk(i))

    {

       cnt++;

    }

    return cnt;

}

 

 

void fix(int &x,int y)//更新dp值

{

    if(y>x) x=y;

}

int update(const int* hp,const int* h,int p )//从状态hp转移到状态h,在第p堆转移。

{

 

    int s,sp=state[hp[1] ][hp[2]  ][hp[3] ][hp[4] ];

 

    int k= a[p][hp[p]];//k表示拿掉糖果的颜色。

 

    if(sp&ysk(k) ) { s=sp^ysk(k);

      fix(  dp[h[1]][h[2]  ][h[3] ][h[4]  ],  dp[hp[1] ][hp[2] ][hp[3]][hp[4]  ]+1    );

    }

    else{

        int num=getNum(sp);

        if(num==4) return 0;

        s=sp^ysk(k);

      fix(  dp[h[1]][h[2]  ][h[3] ][h[4]  ],  dp[hp[1] ][hp[2] ][hp[3]][hp[4]  ]  );

    }

   state[h[1]][h[2]] [h[3]  ][h[4]  ]=s;

    return 1;

 

 

}

 

int work()

{

    int ans=0;

    state[n][n][n][n]=0;

    memset(dp,-1,sizeof dp);

    dp[n][n][n][n]=0;

    int h[5];

    for(h[1]=n;h[1]>=0;h[1]-- ){

        for(h[2]=n;h[2]>=0;h[2]--){

            for(h[3]=n;h[3]>=0;h[3]--){

                for(h[4]=n;h[4]>=0;h[4]--){

                    if(h[1]+h[2]+h[3]+h[4]==4*n)  continue;

//                    dp[h[1] ][h[2]][h[3]][h[4]  ]=-1;

                    for(int i=1;i<=4;i++)  if( h[i]+1<=n  )

                    {

                        int hp[5];

                        memcpy(hp,h,sizeof hp);

                        hp[i]++;

                        if(dp[hp[1]  ][hp[2] ][hp[3]  ][hp[4] ]==-1) continue;

                        update(hp,h,i);

                    }

                    ans=max(ans,dp[h[1] ][h[2]][h[3]][h[4]  ]);

 

                }

            }

        }

    }

    return ans;

}

 

 

int main()

{

    while(~scanf("%d",&n)&&n)

    {

        for(int h=n;h>=1;h-- )

        {

            for(int i=1;i<=4;i++)

            {

                scanf("%d",&a[i][h]);//第i堆,第h层糖果颜色

                a[i][h]--;

            }

        }

        printf("%d\n",work());

    }

 

   return 0;

}

/*

5

1 2 3 4

1 5 6 7

2 3 3 3

4 9 8 6

8 7 2 1

 

对于这组样例的分析:

如果从左往右,高度依次为 0 0 4 5 可以发现这时是个死局

因为篮子里有5颗颜色各不相同的糖果。

可以认为该状态无法达到,因为即使达到了也不能进行后续转移,并且这个状态的最后一步是没有消除糖果的,所以这个状态

相比于前面转移到它的状态并不更优,

所以说可以认为该状态达不到,这对于结果毫无影响。

 

 

假如状态(0 ,0 ,4, 5)无法达到,那么状态(0,0,4,4)还需要继续计算 ,(0,0,4,4)是可以到达的。

 

从结果上看,(0,0,4,4)篮子里只有4颗糖

但是(0,0,4,4)不可能从(0,0,4,5)转移过来的,完全可以从(1,0,4,4)转移过来。最后一步不同,代表着拿取糖果的顺序不同。

 

还有一点需要注意的是,初始化是,应该全部memset(dp,-1,sizeof dp) 否则可能会受到上一组样例的影响

 

比如说考虑结果(0,0,3,3),枚举这个状态的前驱,如果状态(0,0,3,4)在这个样例里是根本不可达的,甚至能达到的状态距离它

 

还相距甚远,但是在上一组样例里这个状态可以达到的,那么它的dp值就不为-1,会从上一个样例的状态(0,0,3,4)转移到这个样例的

 

(0,0,3,3),从而造成错误。

*/
View Code

优质内容筛选与推荐>>
1、db2动态查看备份进度
2、MFC连接SQL2008(ODBC)
3、黑马程序员——java基础之文件复制
4、Android面试收集录17 Android进程优先级
5、让乌龟SVN(TortoiseSVN)提交时忽略bin和obj目录 (转)


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn