cf刷题日记 1.28 ~2.1

Codeforces Round #530 (Div. 2)F. Cookies

https://codeforces.com/contest/1099/problem/F

题意:
给你一棵树,每个节点上有若干饼干,并且给出每个节点上吃一块饼干需要多少时间,同时给出走过一条边所需时间。

总时限为 TT,两个人轮流进行操作:

Mitya从当前节点选择一个子节点向下走,或者直接结束游戏并往根回动吃饼干;

Vasya割断当前节点到其某个子节点的边,或者什么都不做。

问Mitya可以吃到的最多的多少块饼干。

思路参考:Dream_maker_yk的博客

线段树维护前缀和。

首先我们以时间ti为坐标向线段树中插入节点。保存两个值,把子树吃光所用的总时间sum,子树中的cookie总数cnt。

然后根据剩余时间val查询,如果左子树的sum比val小,那么说明左子树可以吃光,那么查询结果就是:

cnt左子树 + 对右子树查询val - sum左子树

这样我们就可以用logn的时间实现贪心策略了。

卡在线段树 。
线段树的下表是时间戳。
就是 吃一块所需要的时间是i 的饼的数量和总消耗时间。

然后 每次就是维护线段树,维护的是dfs一条链的不同饼的和。

#include <bits/stdc++.h>
using namespace std;
#define mo 100005
long long num_x[mo],time_y[mo];
void read(long long a[],long long n)
{
    for(long long i=1;i<=n;i++)
        cin>>a[i];
}
#define mk make_pair
vector< pair<long long ,long long > > tree[mo];

long long s_tree[mo*40],s_trees[mo*40];

void update(long long pos,long long l,long long r,long long x,long long t)
{
    //cout<<pos<<' '<<l<<' '<<r<<endl;
    s_tree[pos]+=t*x;
    s_trees[pos]+=x;
    if(l==r) return ;
    long long mid=(l+r)/2;
    if(t<=mid)
        update(pos*2,l,mid,x,t);
    else
        update(pos*2+1,mid+1,r,x,t);

}

long long query(long long pos,long long l,long long r,long long val)
{
    //cout<<"asd asd     "<<s_tree[pos]<<' '<<s_trees[pos]<<' '<<l<<' '<<r<<' '<<val<<endl;
    if(l==r)
        return min(s_trees[pos],val/l);

    long long mid=(l+r)/2;
    if(s_tree[pos*2]<=val)
        return s_trees[pos*2]+query(pos*2+1,mid+1,r,val-s_tree[pos*2]);
    else
        return query(pos*2,l,mid,val);
}

long long dfs(long long node,long long times)
{
   // cout<<node<<' '<<times<<endl;
    update(1,1,1e6,num_x[node],time_y[node]);
    long long ans=query(1,1,1e6,times);
    long long son,t,f1=0,f2=0,num;
 //cout<<node<<' '<<ans<<' '<<' '<<f1<<' '<<f2<<' '<<times<<' '<<num_x[node]<<' '<<time_y[node]<<endl;
    for(long long i=0;i<tree[node].size();i++)
    {
        son=tree[node][i].first;
        t=tree[node][i].second;
        if(times<2*t) continue ;
        num=dfs(son,times-2*t);
        if(num>f1)
        {
            f2=f1;
            f1=num;
        }
        else if(num>f2)
        {
            f2=num;
        }
    }

    update(1,1,1e6,-num_x[node],time_y[node]);

    if(node==1) return max(f1,ans);
    return max(f2,ans);
}
int main()
{
    long long n,k;
    while(cin>>n>>k)
    {
        memset(num_x,0,sizeof(num_x));
        memset(time_y,0,sizeof(time_y));
        read(num_x,n);
        read(time_y,n);
        //cout<<"asd asd  "<<endl;
        long long x,y;
        for(long long i=2;i<=n;i++)
        {
            cin>>x>>y;
            tree[x].push_back(mk(i,y));
        }
        cout<<dfs(1,k)<<endl;


    }
}

F. MST Unification

https://codeforces.com/contest/1108/problem/F

题解
https://www.cnblogs.com/shuaihui520/p/10320158.html

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 2e5+10;
int n,m,fa[maxn];
struct no{
int u,v,w;
}e[maxn];
bool cmp(no a , no b)
{
    return a.w<b.w;
}
void init(){
for(int i=1 ; i<=n ; i++)
    fa[i]=i;
}

int Find(int x)
{
    return x==fa[x] ? x:fa[x]=Find(fa[x]);
}
void Merge(int x, int y)
{
    int fx = Find(x), fy = Find(y);
    if(fx != fy)
        fa[x] = fy;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0 ; i<m ; i++)
        scanf("%d%d%d",&e[i].u , &e[i].v , &e[i].w);
        sort(e , e+m , cmp);
        int ans=0;
        for(int i=0 ; i<m ; )
        {
            int num=0;
            int j=i;
            while(e[i].w ==e[j].w) ///找到权值相同的尾部坐标
            j++;

            for(int k=i ; k<j ; k++) ///i到j都是边权最小而且都是相同的边
            {
                int fx = Find(e[k].u) , fy=Find(e[k].v);
                if(fx!=fy)///统计可以选择的合法边的数量
                num++;
            }
            ///注意,我们还得判断合法边里的冲突边 ,才是我们想要的答案
            for(int k=i ; k<j ; k++)
            {
                int fx=Find(e[k].u) , fy=Find(e[k].v);
                if(fx!=fy)///从合法边中减去非冲突边(即可以被选入到同一个方案里,不互相冲突的边)
                Merge(fx,fy) , num--;
            }
            i=j;
            ans+=num;
        }
        printf("%d\n",ans);
    }
    return 0;
}

E. Sonya and Matrix Beauty

https://codeforces.com/contest/1080/problem/E

做法第一步 字符串 哈希 各种姿势的哈希都行 我是用的1007进制 长无符号整形

第二步 异或前缀和 快速判断字符串是否符合回文串要求

第三步 枚举矩阵的两边 l r 将一整列放入马拉车算法(manachar) 跑有多少回文串

ans+=p[i]/2
p[i] 代表这个为中心的字符串总长度为p[i] 能产生p[i]/2个回文字符串

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define mo 520
ull hash_str[mo][mo];

ull base=1007,hash_n[mo];
void hashnum()
{
   // cout<<"asd asd  "<<endl;
    hash_n[0]=1;
    for(int i=1;i<26;i++)
    {
        hash_n[i]=hash_n[i-1]*base;
        //cout<<hash_n[i]<<' ';
    }
   // cout<<endl;
}

void HashStr(string &s,ull a[])
{
    ull ans=0;
    for(int i=0;s[i];i++)
    {
        ans=ans+hash_n[s[i]-'a'];
        a[i]=ans;
       // cout<<ans<<' ';
    }
    //cout<<endl;
}
int sum[mo][mo];
bool check (int i,int l,int r)
{
    ull x=sum[i][r]^sum[i][l-1];
    return x==0||(!(x-(x&-x)));
}
vector<ull> manc;
int n,m;
long long p[mo];
long long ans=0;
void manacher()
{
    long long id=0,mid=0;
    for(int i=1;i<=2*n;i++)
    {
        if(manc[i]!=511)
        {
            //p[i]=mx>=i?min (p[id*2-i],mx-i):1;
            if(i<id)
            {
                p[i]=min(p[2*mid-i],id-i);
            }
            else p[i]=1;
            while(manc[i+p[i]]==manc[i-p[i]]&&manc[i+p[i]]!=511&&i-p[i]>0) p[i]++;
            if(p[i]+i>id)
            {
                id=p[i]+i;
                mid=i;
            }
            ans+=p[i]/2;
        }
    }
}
int main()
{
    hashnum();
    while(cin>>n>>m)
    {
        string s;
        for(int i=0;i<n;i++)
        {
            cin>>s;
            HashStr(s,hash_str[i]);//诡异hash
            sum[i][0]=(1<<(s[0]-'a'+1));
            for(int j=0;j<m;j++)
            {
                sum[i][j]=sum[i][j-1]^((1<<(s[j]-'a'+1)));//异或前缀和
            }

        }

        for(int i=0;i<m;i++)
        {
            for(int j=i;j<m;j++)
            {
                manc.clear();
                manc.push_back(511);
                for(int k=0;k<n;k++)
                {
                    manc.push_back(0);
                    if( check(k,i,j) )
                    {
                       // cout<<"s sd  "<<k<<' '<<i<<' '<<j<<endl;
                        manc.push_back(hash_str[k][j]-hash_str[k][i-1]);
                    }
                    else
                    {
                        manc.push_back(511);
                    }
                }
                manc.push_back(0);
               // cout<<"l   r   =  "<<i<<' '<<j<<endl;
               // for(int k=0;k<manc.size();k++) cout<<manc[k]<<' ';cout<<endl;
                manacher();
              //  cout<<"xxxxxxx  "<<ans<<endl;
            }


        }
        cout<<ans<<endl;


    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值