2019 2 14 总结

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;   
}
 

猜你喜欢

转载自www.cnblogs.com/wenjing233/p/10395202.html