1. 程式人生 > >2019 2 14 總結

2019 2 14 總結

-m dfs its 方案 情況 所有 truct n) max

T1
考試的時候想到先把數字排序然後尋找連續的(通過差O(1)判斷)且編號是上升的然後卡在編號要上升這裏(於是就又出了兩道題),但是實際沒有這麽煩。
建一個map f
f[i]代表從頭到i這個數字最長的連續上升子序列是多少
轉移就是f[i]=f[i-1]+1
擴展出題:
有n個數字,每個數字有k種形態,每種形態大小為val
問1:給出m個詢問,問在區間l——r是否存在一種可能的序列為上升序列
1、當詢問較多k較小時,設f[i][j]代表當第i個數選取第j大的取值時,以該數字為結尾的上升序列最長為多少,每次查詢枚舉位置靠後的點的長度能否覆蓋較前那個點
復雜度O(nkk+mk)
2、詢問少k大的情況,貪心從l開始到r每次盡可能選取小且符合要求的數
復雜度O(nm)
問2:問從l到r合法的序列方案數為多少
從l開始dp,設f[i][j]代表當第i個數選取第j大的取值時,以該數字為結尾的上升序列方案數為多少,直接統計可轉移的狀態的和即可,復雜度O(nkk)
(都是水題)

#include<bits/stdc++.h>
using namespace std;
int n;
int num[200009],nt[200009],val[200009];
bool mark[200009];
int ans,st;
map<int,int> mp;
inline int read()
{
    int num=0,fs=1;
    char ch;    
    while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar();
    if(ch==‘-‘) fs=-1,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) num=num*10+ch-‘0‘,ch=getchar();
    return num*fs;
}
int main ()
{
    //freopen("sb.out","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
    {
        num[i]=read();
        mp[num[i]]=mp[num[i]-1]+1;
        if(mp[num[i]]>ans) ans=mp[num[i]],st=num[i]-ans+1;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n&&ans;i++)
    {
        if(num[i]==st) printf("%d ",i),st++,ans--;  
    }
    return 0;   
}
 

T2
核心思想:所有翻倍操作放在一個人身上一定最優
證明 若a2^n+b2^m>c^(n+m)
即在a2^n和b2^m中必有一個大於0.5*c^(n+m)
那將2^(n+m)移到它的身上肯定更值
然後枚舉誰翻倍,註意判斷b的情況即可

#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
    long long num=0,fs=1;
    char ch;    
    while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar();
    if(ch==‘-‘) fs=-1,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) num=num*10+ch-‘0‘,ch=getchar();
    return num*fs;
}
long long nb[21];
long long ans;
long long n,a,b;
struct gzw
{
    long long x,y;  
}baby[200001];
inline long long calc(gzw x)
{
    return x.x-x.y; 
}
inline bool rnk(gzw a,gzw b)
{
    return calc(a)>calc(b); 
}
inline long long ksm(long long num,long long k)
{
    return num*nb[k];
}
int main ()
{
    nb[0]=1;
    for(long long i=1;i<=20;i++)
    {
        nb[i]=nb[i-1]*2;    
    }
    n=read(),a=read(),b=read();
    for(long long i=1;i<=n;i++)
    {
        baby[i].x=read(),baby[i].y=read();
    }
    sort(baby+1,baby+n+1,rnk);
    for(long long i=1;i<=b;i++)
    {
        ans+=max(baby[i].x,baby[i].y);
    }
    for(long long i=b+1;i<=n;i++)
    {
        ans+=baby[i].y; 
    }
    long long sum=ans;
     for (int i=1;i<=b;i++) ans=max(ans,sum-max(baby[i].x,baby[i].y)+(baby[i].x<<a));
    sum=sum-max(baby[b].x,baby[b].y)+baby[b].y;
    for (int i=b+1;i<=n&&b;i++) ans=max(ans,sum-baby[i].y+(baby[i].x<<a));
    printf("%I64d\n",ans);
    return 0;   
}

 

T3
k=1時就是普通的統計路徑數樹形DP即可
當k>1時就是(路徑長度和+每條路徑離被整除的差的和)/k
即長度為x的路徑額外貢獻就為k-x%k(當x=0時沒有貢獻)
cnt[i][j]代表在i節點包括它下面的節點中深度關於k的余數為j的節點數量
轉移就是

 for(int x=0;x<k;x++)
            {
                for(int y=0;y<k;y++)
                {
                    long long tmp=((x+y-(tot<<1))%k+k)%k;
                    tmp=((k-tmp)%k+k)%k;
                    ans+=tmp*cnt[now][x]*cnt[to[i]][y];
                }
            }
            for(int x=0;x<k;x++)
            {
                cnt[now][x]+=cnt[to[i]][x];
            }

別忘了賦初始值

 sum[now]=cnt[now][tot%k]=1;

完整代碼

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int num=0,fs=1;
    char ch;    
    while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar();
    if(ch==‘-‘) fs=-1,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) num=num*10+ch-‘0‘,ch=getchar();
    return num*fs;
}
int sb,n,k;
int head[200009],to[400009],nt[400009],bh;
long long cnt[200009][5],sum[200009];
long long ans;
inline void add(int u,int v)
{
    bh++;
    nt[bh]=head[u];
    head[u]=bh;
    to[bh]=v;
}
void dfs(int now,int fa,int tot)
{
    sum[now]=cnt[now][tot%k]=1;
    for(int i=head[now];i;i=nt[i])
    {
        if(to[i]!=fa) 
        {
            dfs(to[i],now,tot+1);
            sum[now]+=sum[to[i]];
            ans+=sum[to[i]]*(n-sum[to[i]]);
            for(int x=0;x<k;x++)
            {
                for(int y=0;y<k;y++)
                {
                    long long tmp=((x+y-(tot<<1))%k+k)%k;
                    tmp=((k-tmp)%k+k)%k;
                    ans+=tmp*cnt[now][x]*cnt[to[i]][y];
                }
            }
            for(int x=0;x<k;x++)
            {
                cnt[now][x]+=cnt[to[i]][x];
            }
        }
    }
    
}
int main ()
{
    n=read(),k=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read();
        add(u,v);
        add(v,u);
    }
    dfs(1,0,0);
    cout<<(ans/k);
    return 0;   
}
 

2019 2 14 總結