18-2-10 刷題心得

又是刷水題的一天。。。

題目:scoi2005 最大子矩陣
一個比較noip的區間dp,然而我還是腦殘地卡了好幾個小時;
這題數據範圍比較小,有很多不同的做法;
我的做法是dp[i,k,j,o]表示當前掃到了前i行,選了k個矩陣,狀態是j,o的最大值;
j,o的狀態是指如果j=0,第2列第i個位置往前數o行一定沒有被選;j=1,第1列第i個位置往前數o行一定沒有被選;
如果j=2,指怎麼選都可以的最優值;
具體轉移細節看代碼吧。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define LL long long
#define random(a,b) (a+rand()%(b-a+1))
int dp[105][11][3][105];
using std::max;
int n,m,ki;
int ans=0;
int mapp[105][2];
int sum[105][2];
int main()
{
    scanf("%d%d%d",&n,&m,&ki);
    for(int i=1; i<=n; i++)
        for(int j=0; j<m; j++)scanf("%d",&mapp[i][j]);
    for(int j=0; j<m; j++)
        for(int i=1; i<=n; i++)sum[i][j]=mapp[i][j]+sum[i-1][j];
    for(int k=1; k<=ki; k++)
        for(int i=1; i<=n; i++)
            for(int j=0; j<=2 ; j++)
                for(int o=1; o<=i; o++)
                {
                    dp[i][k][j][o]=max(dp[i-1][k][j][o],dp[i][k-1][j][o]);//先繼承之前的狀態
                    dp[i][k][j][o]=max(dp[i][k][j][o],dp[i-1][k][j][o-1]);//如果當前這個點不被選那麼繼承的就是這個狀態
                    for(int p=i-1; p>=0; p--)
                    {
                        int maxval=sum[i][j]-sum[p][j];
                        if(j==2)
                        {
                        //如果j=2,有三種轉移方法:①一種是兩行一起轉移,由於可以隨便選空矩陣,所以maxval在選第1行,第2行,兩行都選的3種情況中取最優
                            maxval=max(sum[i][0]-sum[p][0],sum[i][1]-sum[p][1]);
                            maxval=max(sum[i][0]-sum[p][0]+sum[i][1]-sum[p][1],maxval);

                            dp[i][k][j][o]=max(dp[i][k][j][o],dp[p][k-1][2][o]+maxval);
                            maxval=sum[i][0]-sum[p][0];
                            dp[i][k][j][o]=max(dp[i][k][j][o],dp[i][k-1][1][i-p]+maxval);//②③只選其中的1行,那麼只能從這個點往前數這一行沒被選的那種狀態,例如,是選第一行從第i個點往前p個,那麼需要從i到p這一行都沒被選的那種狀態開始轉移(是行還是列意會就好qwq)
                            maxval=sum[i][1]-sum[p][1];
                            dp[i][k][j][o]=max(dp[i][k][j][o],dp[i][k-1][0][i-p]+maxval);
                        }
                        else
                        {
                            if(i-p<o)
                                dp[i][k][j][o]=max(dp[i][k][j][o],dp[p][k-1][j][o-i+p]+maxval);//如果轉移的斷點還沒有完全達到填滿o這個狀態,只能從o-i+p轉移
                            else
                            {
                                dp[i][k][j][o]=max(dp[i][k][j][o],dp[p][k-1][2][1]+maxval);//否則,因爲有空矩陣,而且對於j=2的情況,o這一維是完全沒用的,所以在加不加maxval中取最優
                                dp[i][k][j][o]=max(dp[i][k][j][o],dp[p][k][2][1]);
                            }
                        }
                    }
                    ans=max(ans,dp[i][k][j][o]);//取最優
                }
    printf("%d\n",ans);
}

題目:noi2015 軟件包管理器
裸的不能再裸比模板題還裸的樹鏈剖分,果然我只能做出來大水題。。。
然而我還是腦殘的其中一個=號寫成+=號。。我說我怎麼線段樹都寫不對了orz

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define LL long long
#define random(a,b) (a+rand()%(b-a+1))
const int maxn=100005;
int n,m;
int d[maxn],siz[maxn],node[maxn],fa[maxn],sum[maxn<<2],col[maxn<<2],tp[maxn],start[maxn],end[maxn],son[maxn];
struct asd
{
    int next,to;    
}edge[maxn]; 
int etot=0;
void add(int x,int y)
{
    edge[++etot].next=node[x];
    node[x]=etot;
    edge[etot].to=y;
}
int dfs(int x)
{
    d[x]=d[fa[x]]+1; 
    siz[x]=1;
    son[x]=0;
    for(int i=node[x];i;i=edge[i].next)
    {
        fa[edge[i].to]=x;
        siz[x]+=dfs(edge[i].to);
        if(siz[son[x]]<siz[edge[i].to])son[x]=edge[i].to;
    }
    return siz[x];
}
int ntot=0;
void dfs1(int x,int top)
{
    start[x]=++ntot;
    tp[x]=top;
    if(son[x])dfs1(son[x],top);
    for(int i=node[x];i;i=edge[i].next)
        if(edge[i].to!=son[x])
            dfs1(edge[i].to,edge[i].to);
    end[x]=ntot;
} 
void updata(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1]; 
}
void color(int l,int r,int rt,int v)
{
    col[rt]=v;
    sum[rt]=(r-l+1)*v; 
}
void pushcol(int l,int r,int rt)
{
    if(col[rt]!=-1)
    {
        int mid=(l+r)>>1;
        color(lson,col[rt]);
        color(rson,col[rt]);
        col[rt]=-1;
    }
}
void build(int l,int r,int rt)
{
    col[rt]=-1;
    if(l==r)
    {
        sum[rt]=0;
        return;
    }
    int mid=(l+r)>>1;
    build(lson),build(rson);
    updata(rt);
}
int query(int l,int r,int rt,int nl,int nr)
{
    if(nl<=l&&nr>=r)
        return sum[rt];
    pushcol(l,r,rt);
    int tmpans=0;
    int mid=(l+r)>>1;
    if(nl<=mid)tmpans+=query(lson,nl,nr);
    if(nr>mid)tmpans+=query(rson,nl,nr);
    return tmpans; 
}
void modify(int l,int r,int rt,int nl,int nr,int v)
{
    if(nl<=l&&nr>=r)
    {
        color(l,r,rt,v);
        return ;
    }
    pushcol(l,r,rt);
    int mid=(l+r)>>1;
    if(nl<=mid)modify(lson,nl,nr,v);
    if(nr>mid)modify(rson,nl,nr,v);
    updata(rt);
}
void install(int x)
{
    int ans=0;
    for(int i=x;i;i=fa[tp[i]])
    {
        ans-=query(1,n,1,start[tp[i]],start[i]);
        ans+=d[i]-d[tp[i]]+1;
        modify(1,n,1,start[tp[i]],start[i],1);
    }
    printf("%d\n",ans);
}
void uninstall(int x)
{
    int ans=query(1,n,1,start[x],end[x]);
    modify(1,n,1,start[x],end[x],0);
    printf("%d\n",ans);
}
int tmp1,tmp2;
char c[20];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d",&tmp1);
        add(tmp1+1,i+1);
    }
    dfs(1);
    dfs1(1,1);
    scanf("%d",&m);
    while(m--)
    {
        scanf("%s%d",c,&tmp1);
        if(c[0]=='i')install(tmp1+1);
        else uninstall(tmp1+1);
    }
    return 0;
} 

題目:noi2016 區間
貌似也是比較簡單的題,但是我還是沒能完全想到正解
將每個區間按大小排序,然後順序插入線段樹維護區間被覆蓋過的最大值,如果最大值大於等於m,求最先插入和最後插入的差,然後把最先插入的抹掉;
還看到有dalao用標記永久化的線段樹 於是模仿着寫了一下,具體思想等以後詳細學了再說吧orz

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define inf 1349880437
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define LL long long
#define random(a,b) (a+rand()%(b-a+1))
using std::min;
using std::max; 
const int maxn=500005;
int n,m;
int a[maxn*2];
int maxv[maxn*8],sum[maxn*8];
int atot=0;
struct asd
{
    int l,r;
    bool operator <(const asd &o)const
    {
        return r-l<o.r-o.l;
    }
}q[maxn];
int findlc(int x)
{
    return std::lower_bound(a+1,a+atot+1,x)-a; 
}
int tot=0;
void modify(int l,int r,int rt,int nl,int nr,int v)
{
    if(nl<=l&&nr>=r)
    {
        sum[rt]+=v;
        maxv[rt]+=v;
        return; 
    } 
    int mid=(l+r)>>1;
    if(nl<=mid)modify(lson,nl,nr,v);
    if(nr>mid)modify(rson,nl,nr,v);
    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1])+sum[rt];
}
int ans=inf;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        atot+=2;
        scanf("%d%d",&a[atot],&a[atot-1]);
        q[i].l=a[atot];
        q[i].r=a[atot-1];
    }
    std::sort(a+1,a+atot+1);
    std::sort(q+1,q+n+1);
    atot=std::unique(a+1,a+atot+1)-a-1;
    int l=1;
    for(int i=1;i<=n;i++)
    {
        int nl=findlc(q[i].l);
        int nr=findlc(q[i].r);
        modify(1,atot,1,nl,nr,1); 
        while(maxv[1]>=m)
        {
            ans=min(ans,q[i].r-q[i].l-q[l].r+q[l].l);
            modify(1,atot,1,findlc(q[l].l),findlc(q[l].r),-1);
            l++;
        }
    } 
    if(ans==inf)ans=-1;
    printf("%d\n",ans);
    return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章