多校五。

HDU-4340-treeDP

这题一看就是树DP。可是当时我想错了。伤心死了。。后来看到解题报告 dp[N][2][2], 瞬间就都懂了 。。 可是没用了 。。 没用了 。。

dp[i][2][2] //一维表示以谁为节点的子树; 二维第几个人; 三位这棵子树有几个全价。

转移方程,以第0个人为例,同理第1个人。j,k是i的不同孩子。

dp[i][0][0] = cost[i][0]/2 + sum( min( dp[j][0][0], dp[j][1][1] ) );

dp[i][0][1] = cost[i][0] + sum( min( dp[j][0][0], dp[j][1][1] ) );

dp[i][0][1] = cost[i][0]/2 + min( dp[k][0][1], sum( dp[j][0][0], dp[j][1][1] ) );

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 1009;
const double esp = 1e-6;
const int N = 1009;

vector<int>v[N];
int dp[N][2][2];
int a[N][2];
int n;

void dfs(int s, int pre)
{
    int i, ss, sum0=0, sum1=0, tmp0=1<<20, tmp1=1<<20;
    for(i=0; i<v[s].size(); i++)
    {
        ss = v[s][i];
        if(ss==pre) continue;
        dfs(ss, s);
        sum0 += min( dp[ss][0][0], dp[ss][1][1] );
        sum1 += min( dp[ss][1][0], dp[ss][0][1] );
        tmp0 = min( tmp0, dp[ss][0][1]-min( dp[ss][0][0], dp[ss][1][1] ) );
        tmp1 = min( tmp1, dp[ss][1][1]-min( dp[ss][1][0], dp[ss][0][1] ) );
    }
    dp[s][0][0] = a[s][0]/2 + sum0;
    dp[s][1][0] = a[s][1]/2 + sum1;
    dp[s][0][1] = min( a[s][0]+sum0, a[s][0]/2+tmp0+sum0 );
    dp[s][1][1] = min( a[s][1]+sum1, a[s][1]/2+tmp1+sum1 );
}

int main()
{
    int i, x, y;
    while(~scanf("%d", &n))
    {
        for(i=1; i<=n; i++) scanf("%d", &a[i][0]);
        for(i=1; i<=n; i++) scanf("%d", &a[i][1]);
        for(i=1; i<=n; i++) v[i].clear();
        for(i=1; i<n; i++)
        {
            scanf("%d%d", &x, &y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        dfs(1, 0);
        printf("%d\n", min(dp[1][1][1], dp[1][0][1]));
    }
    return 0;
}

HDU-4341-分组背包

排序(先取近的),把共线的点分为一组,然后就是分组背包了 。。。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <vector>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int maxn = 209;
const int maxt = 40009;

int dp[maxt];
int N, T;
vector<int>g[maxn];

struct point
{
    int x, y, t, v, grp;
}p[maxn];

int cmp(point a, point b)
{
    return a.x<b.x;
}

int main()
{
    int ca = 0, cnt, i, j, k;
    while(~scanf("%d%d", &N, &T))
    {
        for(i=1; i<=N; i++)
        {
            scanf("%d%d%d%d", &p[i].y, &p[i].x, &p[i].t, &p[i].v);
            p[i].grp = 0;
            g[i].clear();
        }
        sort(p+1, p+N+1, cmp);
        p[1].grp = cnt = 1;
        g[1].push_back(1);
        for(i=1; i<=N; i++)
        {
            for(j=i+1; j<=N; j++)
            if(p[j].grp==0)
            {
                if( p[i].x*p[j].y == p[i].y*p[j].x )
                {
                    p[j].grp = p[i].grp;
                    g[ p[i].grp ].push_back(j);
                }
            }
            if(i<N && p[i+1].grp==0)
            {
                p[i+1].grp = ++cnt;
                g[cnt].push_back(i+1);
            }
        }

        memset(dp, 0, sizeof(dp));
        for(i=1; i<=cnt; i++) //groups
        {
            for(k=T; k>=0; k--)
            {
                int t = 0, v = 0;
                for(j=0; j<g[i].size(); j++)
                {
                    int id = g[i][j];
                    t += p[id].t;
                    v += p[id].v;
                    if(k>=t) dp[k] = max(dp[k], dp[k-t]+v);
                    else break;
                }
            }
        }
        printf("Case %d: %d\n", ++ca, dp[T]);
    }
    return 0;
}

HDU-4342-

求第n个非平方数,和大于等于这个数的开方和。n为int型。

这题不会,看的别人的解题思路。

http://blog.youkuaiyun.com/wmn_wmn/article/details/7840712


HDU-4343-倍增算法

排序,去掉覆盖其他点的线,离散化思想。然后变成s和t都是上升的序列。

prev[x][k] 表示 第x个点,向前2^k个不重复线的最后位置。

如 prev[x][0] = max( y(y<x && t[y]<=s[x]) )

递推公式: prev[x][k] = prev[prev[x][k-1]][k-1];

对询问排序,二分模拟答案。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int INF = 1000000001;

struct line
{
    line() {}
    line(int a, int b): s(a), t(b) {}
    int s, t;
    bool vis;
    bool operator<(const line &a)const
    {
        return s<a.s;
    }
}li[100010], aft[100010];

bool cmp(const line &u, const line &v)
{
    if (u.t==v.t) return u.s>v.s;
    return u.t<v.t;
}

struct sq
{
    int id;
    int s, t;
    bool operator<(const sq &a) const
    {
        return t<a.t;
    }
}q[100010];

int prev[100010][22];
int ans[100010];
int N, M;

int main()
{
    int i, j, k;
    while (~scanf("%d%d", &N, &M))
    {
        for (i = 0; i < N; ++i)
        {
            scanf("%d%d", &li[i].s, &li[i].t);
            li[i].vis = 0;
        }
        sort(li, li+N, cmp);
        for (i = 0; i < N; )
        {
            j = i+1;
            while (j<N && li[j].s <= li[i].s) li[j++].vis = 1;
            i = j;
        }
        j = 0;
        for (i = 0; i < N; ++i)
            if (!li[i].vis) aft[++j] = li[i];
        N = j;
        sort(aft+1, aft+N+1);
        aft[0] = line(-2, -1);

        for (i=0,j=1; j<N; ++i,j<<=1);
        int bmax = i;

        for (k = i = 1; i <= N; ++i) 
        {
            while (aft[k].t <= aft[i].s) k++;
            prev[i][0] = k-1;//i之前和i最近的不重叠的线
        }
        prev[0][0] = 0;
        for (j = 1; j <= bmax; ++j)
        {
            prev[0][j] = 0;
            for (i = 1; i <= N; ++i)
            {
                prev[i][j] = prev[prev[i][j-1]][j-1];
            }
        }

        for (i = 0; i < M; ++i)
        {
            scanf("%d%d", &q[i].s, &q[i].t);
            q[i].id = i;
        }
        sort(q, q+M);

        k = 0;
        int b;
        for (i = 0; i < M; ++i)
        {
            while (k<=N && aft[k].t <= q[i].t) k++;
            int &res = ans[q[i].id], now = k-1;
            res = 0;
            for (j=bmax; j>=0; j--)
            {
                if (aft[prev[now][j]].s>=q[i].s)
                {
                    res |= (1<<j);
                    now = prev[now][j];
                }
            }
            if (aft[k-1].s >= q[i].s) res++;
        }

        for (i = 0; i < M; ++i)
            printf("%d\n", ans[i]);

    }
    return 0;
}

HDU-4344-大数素数分解

不知怎么描述题意。如198=2*3*3*11,则ans(198)=2+9+11=22;  8=2*2*2 而ans(8)=4

这题到现在也不明白,贴个代码当作大数分解的模版吧。(随机数)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define MAXN 10

__int64 minn;

__int64 multi(__int64 a,__int64 b,__int64 n)
{
    __int64 temp=a%n,s=0LL;
    while(b)
    {
       if(b&1LL)s=(s+temp)%n;
       temp=(temp+temp)%n;
       b>>=1;
    }
    return s;
}

__int64 Pow(__int64 a,__int64 b,__int64 n)
{
    __int64 temp=a%n,s=1LL;
    while(b)
    {
       if(b&1LL)s=multi(s,temp,n);
       temp=multi(temp,temp,n);
       b>>=1;
    }
    return s;
}

__int64 Pow2(__int64 a,__int64 b)
{
    __int64 s = 1LL;
    for(int i = 0; i < b; ++i)
        s *= a;
    return s;
}

int witness(__int64 a,__int64 n)
{
    __int64 u=n-1LL,t=0LL,i,x,y;
    while(!(u&1LL))u>>=1,t++;
    x=Pow(a,u,n);
    for(i=1;i<=t;i++)
    {
        y=multi(x,x,n);
        if(y==1LL && x!=1LL && x!=n-1LL)return 1;
        x=y;
    }
    if(x!=1LL) return 1;
    return 0;
}

int test(__int64 n)
{
    __int64 a;
    int i;
    if(n==2LL) return 1;
    if(n<2LL||!(n&1ll))return 0;
    for(i=0;i<MAXN;i++)
    {
       a=(__int64) rand()%(n-2)+2;
       if(witness(a,n))return 0;
    }
    return 1;
}

__int64 gcd(__int64 a,__int64 b)
{
    return b?gcd(b,a%b):a;
}

__int64 pollard_rho(__int64 n,__int64 c)
{
    __int64 x, y, d, i=1LL, k=2LL;
    x=((__int64)rand())%(n-1LL)+1LL;
    y=x;
    while(1)
    {
        ++i;
        x=(multi(x,x,n)+c)%n;
        d=gcd(y-x+n,n);
        if(d!=1 && d!=n) return d;
        if(x==y)return n;
        if(i==k)
        {
            y=x;
            k<<=1;
        }
    }
}

void find(__int64 n,__int64 c = 70ll)
{
     __int64 r;
     if(n<=1LL) return ;
     if(test(n))
     {
       if(minn>n)minn=n;
       return ;
     }
     r=pollard_rho(n,c--);
     find(n/r,c);
     find(r,c);
}

__int64 top;
__int64 vec[1000000][2];
__int64 pri[] = {2,3,5,7,11,13,17,19,23,29};

int main()
{
    __int64 n,ans,cnt;
    int t,i;
    scanf("%d",&t);

    while(t--)
    {
        top  = 0;
        scanf("%I64d",&n);
        for(i = 0; i < 10; ++i)
        {
           cnt = 0;
           while(n % pri[i] == 0)
           {
               ++cnt;
               n /= pri[i];
           }
           if(cnt) vec[top][0] = pri[i],vec[top++][1] = cnt;
        }
        while(n!=1)
        {
            minn=n;
            find(n);
            cnt =0;
            while(n % minn == 0)
            {
                ++cnt;
                n /= minn;
            }
            vec[top][0] = minn,vec[top++][1] = cnt;
        }
        ans = 0;
        if(top == 1) vec[0][1] --;
        for(i = 0; i < top; ++i) ans += Pow2(vec[i][0],vec[i][1]);
       printf("%I64d %I64d\n",top, ans);
    }
    return 0;
}


HDU-4345-记忆化DP 求循环节

当时怎么就没想到呢 。。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int inf = 0x3f3f3f3f;
const int N = 1100;
const double esp = 1e-6;

int pri[N], vis[N];
__int64 ans[N][N];
int cnt;

void prime()
{
    int i, j;
    cnt = 0;
    for(i=2; i<N; i++)
    if(!vis[i])
    {
        pri[cnt++] = i;
        for(j=i+i; j<N; j+=i) vis[j] = 1;
    }
}

__int64 dp(int m, int n, int i)
{
    if(ans[n][i]!=-1) return ans[n][i];
    if(i>m) return ans[n][i] = 1;
    ans[n][i] = dp(m, n, i+1);//首先是第i个质数不存在的情况
    int k = pri[i];
    while(k<=n)
    {
        ans[n][i] += dp(m, n-k, i+1);//然后枚举他的幂次数存在
        k *= pri[i];
    }
    return ans[n][i];
}

int main()
{
    int i, n, m;
    prime();
    while(~scanf("%d", &n))
    {
        for(i=0; pri[i]<=n; i++);
        m = i-1;
        memset(ans, -1, sizeof(ans));
        printf("%I64d\n", dp(m, n, 0));
    }
    return 0;
}

HDU-4348-线段树

离线线段树 。。 解题报告中的做法都不会 。。 T_T

离线,两个原因。

1.查询某历史事件的都单独存起来,到那个时间就求一下, 而不等到真正查询的时候再求。

2.回到某一历史事件。把前面的各种操作都存起来,回去的时候,把修改操作反修改一边。这里用一个to[]数组表示第i条操作的前一条。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int N = 100009;

__int64 val[N<<2], lazy[N<<2], ans[N];
int p[N], to[N];

struct que
{
    char op[3];
    int l, r, t;
    __int64 d;
}q[N];

bool cmp(int a, int b)
{
    return q[a].t<q[b].t;
}

void build(int l, int r, int k)
{
    lazy[k] = 0;
    if(l==r)
    {
        scanf("%I64d", &val[k]);
        return;
    }
    int mid = (l+r)>>1;
    build(l, mid, k<<1);
    build(mid+1, r, k<<1|1);
    val[k] = val[k<<1] + val[k<<1|1];
}

void updata(int l, int r, __int64 d, int ll, int rr, int k)
{
    val[k] += d*(r-l+1);
    if(l==ll && r==rr)
    {
        lazy[k] += d;
        return;
    }
    int mid = (ll+rr)>>1;
    if(r<=mid) updata(l, r, d, ll, mid, k<<1);
    else if(l>mid) updata(l, r, d, mid+1, rr, k<<1|1);
    else
    {
        updata(l, mid, d, ll, mid, k<<1);
        updata(mid+1, r, d, mid+1, rr, k<<1|1);
    }
}

void down(int ll, int rr, int k)
{
    int mid = (ll+rr)>>1;
    val[k<<1] += lazy[k]*(mid-ll+1);
    val[k<<1|1] += lazy[k]*(rr-mid);
    lazy[k<<1] += lazy[k];
    lazy[k<<1|1] += lazy[k];
    lazy[k] = 0;
}

__int64 query(int l, int r, int ll, int rr, int k)
{
    if(lazy[k]!=0)
    {
        if(ll==rr) lazy[k] = 0;
        else down(ll, rr, k);
    }
    if(l==ll && rr==r) return val[k];
    int mid = (ll+rr)>>1;
    if(r<=mid) return query(l, r, ll, mid, k<<1);
    else if(l>mid) return query(l, r, mid+1, rr, k<<1|1);
    else return query(l, mid, ll, mid, k<<1) + query(mid+1, r, mid+1, rr, k<<1|1);
}

int find(int l,int r,int t)
{
    int m;
    while(l<=r)
    {
        m=(l+r)>>1;
        if(q[p[m]].t>=t) r=m-1;
        else l=m+1;
    }
    return r;
}

int main()
{
    int n, m, i, j;
    while(~scanf("%d%d", &n, &m))
    {
        build(1, n, 1);
        int h=0;
        for(i=0; i<m; i++)
        {
            scanf("%s", q[i].op);
            switch(q[i].op[0])
            {
                case 'C':scanf("%d%d%I64d", &q[i].l, &q[i].r, &q[i].d); break;
                case 'Q':scanf("%d%d", &q[i].l, &q[i].r); break;
                case 'H':scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].t); p[h++]=i; break;
                case 'B':scanf("%d", &q[i].t); break;
            }
            to[i] = i-1; //初始就是他的前一位
        }
        sort(p, p+h, cmp); //按时间排序
        int t = 0, hh = 0;
        for( ; hh<h; hh++)
        {
            int id = p[hh];
            if(q[id].t < t) continue;
            else if( q[id].t == t ) ans[id] = query( q[id].l, q[id].r, 1, n, 1 ) ;
            else break;
        }


        for(i=0; i<m; i++) //逐条操作
        {
            if(q[i].op[0]=='C')
            {
                updata(q[i].l, q[i].r, q[i].d, 1, n, 1);
                t++;
                for(hh=find(i+1, h-1, t) ; hh<h; hh++)
                {
                    int id = p[hh];
                    if(q[id].t < t) continue;
                    else if( q[id].t == t ) ans[id] = query( q[id].l, q[id].r, 1, n, 1 ) ;
                    else break;
                }
            }
            else if(q[i].op[0]=='Q') printf("%I64d\n", query(q[i].l, q[i].r, 1, n, 1));
            else if(q[i].op[0]=='H') printf("%I64d\n", ans[i]);
            else
            {
                j = to[i];
                while(j>=0 && t>q[i].t)
                {
                    if(q[j].op[0]=='C') updata(q[j].l, q[j].r, -q[j].d, 1, n, 1), --t;
                    j = to[j];
                }
                to[i] = j;
                for(hh=find(i+1, h-1, t) ; hh<h; hh++)
                {
                    int id = p[hh];
                    if(q[id].t < t) continue;
                    else if( q[id].t == t ) ans[id] = query( q[id].l, q[id].r, 1, n, 1 ) ;
                    else break;
                }
            }
        }

    }

    return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值