2017 多校 3(6056-6066)进度(5/11)

A - simple counting problem HDU - 6056

待补

B - Kanade’s convolution HDU - 6057

待补

C - Kanade’s sum HDU - 6058 (模拟队列/链表)

题意:给n个数是(1-n),然后问所有序列中第k大的数之和是多少。

当时和队友先找规律找半天,然后觉得不对。之后就怎么想都超时。

赛后看到有人模拟做的,自己先模拟了一下,还是超时,直接模拟太暴力了。从第一个数开始考虑,当这个数是第k大的时候,先找左边它大的,找到k-1个,然后往左边找。注意一下边界。。
我的是左边来一下,右边来一下,肯定超时啦。。

第二种方法是用链表,从小到大扫,然后扫完之后删除节点就行了。直接记录位置,扫的当前点,与它相连的都是比它大的,不用扫比它小的了,因为比它小的已经删除了。

//直接模拟(TIME:1934ms)差一点就超时了//用c++交会tle ,用G++交

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>

using namespace std;
#define LL long long
const int maxn = 1000020;
LL a[maxn];
int b[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d %d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        LL ans=0;
        for(int i=1;i<=n;i++)
        {
            int h=0;
            b[++h]=i;
            int j;
            for(j=i+1;j<=n&&h<k;j++)
            {
                if(a[j]>a[i]) b[++h]=j;
            }
            if(h>=k)
            {
                LL temp=1,t=0;
                for(;j<=n;j++)
                {
                    if(a[j]>a[i]) break;
                    else temp++;
                }
                t+=temp;
                for(j=i-1;j>=1;j--)
                {
                    if(a[j]>a[i]&&h<=1) break;
                    else if(a[j]>a[i])
                    {
                        temp=b[h]-b[h-1];
                        h--;
                        t+=temp;
                    }
                    else t+=temp;
                }
                ans+=t*a[i];
            }
            else {
                int g=h;
                for(j=i-1;j>=1;j--)
                {
                    if(a[j]>a[i]) b[++g]=j;
                    if(g==k) break;
                }
                if(g<k) continue;
                int temp=n-b[h]+1,t=temp;
                for(j=j-1;j>=1;j--)//这里为j-1//因为这个WA了好多次
                {
                    if(a[j]>a[i])
                    {
                        if(h<=1) break;
                        temp=b[h]-b[h-1];
                        h--;
                        t+=temp;
                    }
                    else t+=temp;
                }
                ans+=t*a[i];
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

//链表//(TIME:546ms)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>

using namespace std;
#define LL long long
const int maxn = 5e5+10;
int pre[maxn],nxt[maxn],pos[maxn];
int r[maxn],l[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d %d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            pos[x]=i;
            pre[pos[x]]=i-1;
            nxt[pos[x]]=i+1;
        }
        LL ans=0;
        for(int i=1;i<=n;i++)
        {
            int now=pos[i],right=0,left=0;
            for(int j=now;j<=n&&right<k;j=nxt[j])
            {
                r[right++]=nxt[j]-j;
            }
            LL num=0;
            for(int j=now;j>=1&&left<k;j=pre[j])
            {
                if(left+right>=k){
                    l[left]=j-pre[j];
                    num+=l[left]*r[k-left-1];
                    left++;
                }
                else
                {
                    left++;
                    continue;
                }
            }
            ans+=(LL)num*i;
            nxt[pre[pos[i]]]=nxt[pos[i]];
            pre[nxt[pos[i]]]=pre[pos[i]];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D - Kanade’s trio HDU - 6059 (01字典树)

题意:给你一个数组A[n]下标从1开始到n,问有多少个三元组(i,j,k),i < j < k,使得(A[i]^A[j])<(A[j]^A[k])

第一反应是枚举j,然后j的前面都放在一个字典树Trie1中,j的后面后放在一个字典树Trie2中。然后发现没有办法去统计个数。了别人题解之后知道了~~ 满足条件的三元组,如果A[i],A[k]的前10位都相同,第11位不同了
第11位,A[i]=0,A[k]=1,那么A[j]=0,之后的值都可以任意取了。
A[i]=1,A[k]=0,那么A[j]=1,之后的值也可以任意取了。
所以我们用找A[i],A[k]第一个不同的位置,然后统计有多少那个j就行了。

dalao的思路:先从小到大枚举k,把A[k]一个一个插入字典树中,然后相应位统计有多少个j。这样就保证了j < k

然后从小到大枚举i,将以i作为k的对应位数有多少个j减去,这样就保证了i < k.(相当于k的范围在(i,k] ),现在j的范围是[1,k),对于当前i,相应位的j我们要减去i出现的次数就可以保证i

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;
#define LL long long
const int maxn = 500005;
int a[maxn];
int node[maxn*10][2];
LL val[maxn*10][2];
LL num[32][2];
LL coun[maxn*10][2];
int tot=1;
void sert(int x,int d)
{
    int u=0;
    for(int i=30;i>=0;i--)
    {
        int g=(x>>i)&1;
        if(!node[u][g])
        {
            node[u][g]=tot++;
        }
        coun[u][g]+=d*num[i][1-g];//如果这里是num[u][1-g]的话,会TLE,因为这样的话num的空间就比较大了,memset就比较耗时.
        val[u][g]+=d;
        num[i][g]++;
        u=node[u][g];
    }
}
LL lookf(int x)
{
    LL sum=0;
    int u=0;
    for(int i=30;i>=0;i--)
    {
        int g1=(x>>i)&1;
        if(node[u][1-g1])
            sum+=coun[u][1-g1]-(LL)val[u][1-g1]*num[i][g1];//这里要防止爆int
        u=node[u][g1];
    }
    return sum;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        tot=1;
        memset(val,0,sizeof(val));
        memset(node,0,sizeof(node));
        memset(num,0,sizeof(num));
        memset(coun,0,sizeof(coun));
        memset(a,0,sizeof(a));
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            sert(a[i],1);
        }
        LL ans=0;
        memset(num,0,sizeof(num));
        for(int i=0;i<n;i++)
        {
            sert(a[i],-1);
            ans+=lookf(a[i]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

思考:num[i][g]表示第i位上为g的个数,那么我计算j的时候应该会有重复的值。之后好像又减去了。。这一点还没怎么明白== (望路过的dalao们指点指点

感觉还是用指针写比较清楚点

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;
#define LL long long
const int maxn = 500005;
int a[maxn];

struct Node
{
    Node *son[2];
    int coun;
    int val;
    node(){
       coun=0,val=0;son[0]=son[1]=NULL;
    }
};
Node *root;
int node[maxn*10][2];
LL val[maxn*10][2];
LL num[32][2];
LL coun[maxn*10][2];
int tot=1;
void sert(int x,int d)
{
    Node *u=root;
    for(int i=30;i>=0;i--)
    {
        int g=(x>>i)&1;
        if(u->son[g]==NULL)
        {
            u->son[g]=new Node();
        }
        u=u->son[g];
        u->coun+=d*num[i][1-g];
        u->val+=d;
        num[i][g]++;
    }
}
LL lookf(int x)
{
    LL sum=0;
    Node *u=root;
    for(int i=30;i>=0;i--)
    {
        int g1=(x>>i)&1;
        Node *p=u->son[1-g1];
        //if(!val[u][g1]) break;
        if(p)
            sum+=p->coun-(LL)p->val*num[i][g1];
        u=u->son[g1];
        if(u==NULL) break;
    }
    return sum;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        root=new  Node();
        int n;
         memset(num,0,sizeof(num));
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            sert(a[i],1);
        }
        LL ans=0;
         memset(num,0,sizeof(num));
        for(int i=0;i<n-2;i++)
        {
            sert(a[i],-1);
            ans+=lookf(a[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

E - RXD and dividing (贪心+dfs)

题意:给你一棵树,将除1之外的点分成k份,每份使之连通(会加一些边),边权之和最大为多少。

很巧妙。这个就是要算每条边的最大贡献。假 设x是根,有y个孩子(所有孩子,孩子的孩子也算),这个时候上面的那条边v的贡献为v*min(y+1,k)因为自己也要算

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
using namespace std;
#define LL long long
const int maxn = 1e6+10;
struct node
{
    int to;
    LL v;
    node(int a=0,LL b=0){
        to=a,v=b;
    }
};
vector<node> vec[maxn];
int vis[maxn];
void addedge(int from,int to,LL v)
{
    vec[from].push_back(node(to,v));
    vec[to].push_back(node(from,v));
}
LL ans=0;
int n,k;

int dfs(int pos,LL zz)
{
    int hz=0;
    for(int i=0;i<vec[pos].size();i++)
    {
        node e=vec[pos][i];
        if(!vis[e.to])
        {
            hz++;
            vis[e.to]=1;
            hz=hz+dfs(e.to,e.v);
        }
        if(i==vec[pos].size()-1){
        int b=hz+1;
        LL g=min(k,b);
        ans+=g*zz;
        }
    }
    return hz;
}
void init()
{
    memset(vis,0,sizeof(vis));
    for(int i=0;i<maxn;i++)
        vec[i].clear();
}
int main()
{
    while(scanf("%d %d",&n,&k)!=EOF)
    {
        init();
        for(int i=1;i<n;i++)
        {
            int from,to;LL v;
            scanf("%d %d %I64d",&from,&to,&v);
            addedge(from,to,v);
        }
        ans=0;
        vis[1]=1;
        dfs(1,0);
        printf("%I64d\n",ans);
    }
    return 0;
}

F - RXD and functions HDU - 6061 (快速数论变换(NTT))

待补

G - RXD and logic gates HDU - 6062

待补

H - RXD and math HDU - 6063 (思维+快速幂)

开始还以为和莫比乌斯反演有关,然后开始推,推半天。然后想打个表看看,结果发现了规律,答案就是n^k%mod,快速幂搞一下就行了。

I - RXD and numbers HDU - 6064

待补

J - RXD, tree and sequence HDU - 6065 (决策单调性优化DP)

待补

K 签到

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值