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

来源:互联网 发布:数据分析师证书难不难 编辑:程序博客网 时间:2024/05/01 09:40

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 longconst 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 longconst 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 longconst 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 longconst 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 longconst 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 签到

原创粉丝点击