2016wust暑假集訓之題解彙總2

數據結構java

hdu4339(樹狀數組+二分)node

題意:給出長度分別爲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(有序表線段樹)ios

解析:這是我之前沒有接觸過的線段樹類型,有序表線段樹,每一個節點申請了兩段空間,主要是爲了保存左邊兒子會有多少比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自動機模板ide

//參照《算法競賽入門經典訓練指南》
#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(線段樹區間異或)ui

/*
題意:給出了幾種集合運算,每次操做對一段區間進行其中的一種運算
輸出最後被覆蓋的區間段,要注意開區間與閉區間

解析:注意一下每種運算如何修改區間的值,在線段樹裏再增長兩個變量
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(樹狀數組)spa

給你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

Codeforces 276 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,如今每次操做爲從一、3號位置上等機率選擇一個與2號位置的杯子交換(換杯子,位置編號仍是不變,可認爲左、中、右三個,好比3換到1就是右邊的杯子換到中間),求n次操做後key在2號位子的杯子裏的機率,結果用分數。
題解:每次都是從一、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
相關文章
相關標籤/搜索