【zzulioj】河南多校训练(985专场)<位运算--DP---思维--扩展欧几里德---回溯---字典树---状态优化>

来源:互联网 发布:《算法时代》 编辑:程序博客网 时间:2024/05/17 08:36

Problem B: 985的数学难题

Time Limit: 2 Sec  Memory Limit: 128 MB
Submit: 85  Solved: 13

SubmitStatusWeb Board

Description

985有n个正整数,他想快速知道下面函数的返回值
int a[N+1];
long long Solve() {
    int i, j;
    long long ans = 0;
    for(i = 1; i <= N; i++) {
     for(int j = i + 1; j <= N; j++) {
    ans += a[i] + a[j] + (a[i] ^ a[j]) + (a[i] | a[j]) + (a[i] & a[j]);
}
    }
    return ans;
}
注:^表示异或运算。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行输入一个整数代表元素个数,接下来一行输入n个正整数a[]。
注:1 <= t <= 30,1 <= n,a[] <= 100000。

Output

一个整数代表最后的返回值ans。

Sample Input

2
1
10
2
1 1

Sample Output

0
4

HINT


两个数的&运算+两个数的^运算=两个数的|运算--

先求各数之和--然后求位运算的值:

位运算的求法可以分位求--

从数大到小--(这样位数不会减少)

每位分为0和1--计算与后面数的位运算


代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define LL long longLL n,s,shu[100100],ge[32];LL ssss[32];void ss(int xx){    int lp=0;    while (xx)    {        if (xx%2)        ge[lp]++;        lp++;        xx/=2;    }}bool cmp(int xx,int yy){    return xx>yy;}void sl(int xx,int pp){    int lp=0;    while (xx)    {        if (xx%2)        {            ge[lp]--;            s+=pp*ssss[lp]*2;//||        }        else        {            s+=ge[lp]*ssss[lp]*2;//||         }        xx/=2;        lp++;    }}int main(){    //   |=&+^;====2*a|b    ssss[0]=1;    for (int i=1;i<30;i++)    ssss[i]=ssss[i-1]*2;    int t;scanf("%d",&t);    while (t--)    {        scanf("%d",&n);s=0;        for (int i=0;i<n;i++)        {            scanf("%d",&shu[i]);            s+=(n-1)*shu[i];        }        //转化为2个2个的| 运算---         sort(shu,shu+n,cmp);        memset(ge,0,sizeof(ge));        for (int i=0;i<n;i++)            ss(shu[i]);        int op=1;        for (int i=0;i<n-1;i++ )        {        //  printf("%d  66\n",shu[i]);            sl(shu[i],n-1-i);        }         printf("%lld\n",s);    }    return 0;}/**************************************************************    Problem: 1893    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:942 ms    Memory:1660 kb****************************************************************/



Problem C: 985的方格难题

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 330  Solved: 62

SubmitStatusWeb Board

Description

985走入了一个n * n的方格地图,他已经知道其中有一个格子是坏的。现在他要从(1, 1)走到(n, n),每次只可以向下或者向右走一步,问他能否到达(n,n)。若不能到达输出-1,反之输出到达(n,n)的方案数。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行输入三个整数n,x,y,分别代表方格地图的大小以及坏掉格子的位置。
注:1 <= t <= 20,1 <= n <= 30,1 <= x,y <= n。

Output

若可以到达(n,n)则输出方案数对1e9 + 7取余的结果,反之输出-1。

Sample Input

2
2 1 2
2 2 2

Sample Output

1
-1

HINT

简单DP--找到动态方程: ma[i][j]=ma[i-1][j]+ma[i][j-1];

不要忘了取余---因为这个我wrong了几次----尴尬

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define LL long longLL ma[50][50];int main() {    int t;scanf("%d",&t);    while (t--)    {        int n,x,y;        scanf("%d%d%d",&n,&x,&y);        memset(ma,0,sizeof(ma));        for (int i=1;i<=n;i++)        {            for (int j=1;j<=n;j++)            {                if (i==x&&j==y)                ma[i][j]=0;                else if (i==1&&j==1)                ma[i][j]=1;                else                ma[i][j]=ma[i-1][j]+ma[i][j-1];            }        }        if (ma[n][n])        {            ma[n][n]=ma[n][n]%1000000007;            printf("%lld\n",ma[n][n]);        }        else        printf("-1\n");    }    return 0;}/**************************************************************    Problem: 1894    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:0 ms    Memory:892 kb****************************************************************/



Problem D: 985的0-1串难题

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 111  Solved: 19

SubmitStatusWeb Board

Description

985有一个长度为n的0-1串,已知他最多可以修改k次(每次修改一个字符即0->1 或者 1->0),他想知道连续的全1子串最长是多少。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行输入两个整数n,k分别代笔上面的信息。
注:1 <= t <= 12,1 <= n <= 100000,0 <= k <= 100000。

Output

一个整数代表可以得到的最大长度。

Sample Input

2
6 3
010100
6 2
010100

Sample Output

5
4

HINT

记录一下0的位置--

如果o的个数小于等于k直接输出n

否则求第i个0到第i+k+1个0之间的数--(他们之间有k个0---)

记得在最开始和结束的位置加上两个0---便于计算最前和最后k个0的情况--

代码:

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;#define LL long longint shu[100100];int he[100100];int main() {    int t;scanf("%d",&t);    while (t--)    {        char ch[100100];        int n,k,kp;        scanf("%d%d",&n,&k);        scanf("%s",ch);        kp=0;        shu[kp++]=0;        for (int i=0;i<n;i++)        if (ch[i]=='0')        shu[kp++]=i+1;        if (kp-1<=k)        printf("%d\n",n);        else        {            int mi=-1;            shu[kp++]=n+1;            for (int i=k+1;i<kp;i++)            {             // printf("%d %d  %d  %d\n",i,i-k-1,shu[i],shu[i-k-1]);                 mi=max(shu[i]-shu[i-k-1]-1,mi);            }             printf("%d\n",mi);        }    }    return 0;}/**************************************************************    Problem: 1895    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:13 ms    Memory:1676 kb****************************************************************/


Problem E: 985的买饮料难题

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 226  Solved: 119

SubmitStatusWeb Board

Description

天气太热了,985制定了对未来的规划即第i天喝a[i]瓶饮料。为了节约开支,他打听到了第i天每瓶饮料的价格p[i]。
为了不让自己的规划落空,他想知道这n天他至少要花多少钱。
精明的985可以选择在任意一天买数目不限的饮料,而且他有一个神奇的冰箱,可以将提前购买的饮料保存下来(至少在这n天里是可以饮用的)。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行输入一个整数n代表规划的总天数,接下来有n行,每行输入两个整数a[],p[]分别代表上面提到的信息。
注:1 <= t <= 30,1 <= n <= 1000,1 <= a[],p[] <= 100。

Output

输出一个整数代表最后的结果。

Sample Input

2
2
25 56
94 17
5
39 21
95 89
73 90
9 55
85 32

Sample Output

2998
6321

HINT

记录一下最小单价就行了---(签到题)

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define LL long longint main() {    int t;scanf("%d",&t);    while (t--)    {        int n;scanf("%d",&n);        int mi=9999,s=0;        int a,b;        for (int i=0;i<n;i++)        {            scanf("%d%d",&a,&b);            mi=min(b,mi);            s+=a*mi;        }        printf("%d\n",s);    }    return 0;}/**************************************************************    Problem: 1896    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:7 ms    Memory:872 kb****************************************************************/


Problem F: 985的红绿灯难题

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 205  Solved: 126

SubmitStatusWeb Board

Description

已知红、绿、黄灯各持续r、g、y秒,985发现此时灯刚刚变绿,他想知道x秒后灯是什么颜色。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据输入四个整数x,g,y,r。
注:1 <= t <= 30,1 <= x,g,r,y <= 1e9。

Output

若x秒后变成红色输出R,变成绿色输出G,反之输出Y。

Sample Input

3
5 5 2 8
7 5 2 8
16 5 2 8

Sample Output

Y
R
G

HINT

 红绿灯顺序:绿->黄->红->绿-------(签到题)


代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define LL long longint main() {    int t;scanf("%d",&t);    while (t--)    {        LL x,g,y,r,s;        scanf("%lld%lld%lld%lld",&x,&g,&y,&r);        s=g+y+r;        x%=s;        if (x<g)        printf("G\n");        else if (x<g+y)        printf("Y\n");        else        printf("R\n");    }    return 0;}/**************************************************************    Problem: 1897    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:0 ms    Memory:872 kb****************************************************************/


Problem G: 985的数字难题

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 256  Solved: 85

SubmitStatusWeb Board

Description

985有n个数,已知每次操作可以将其中不相同的两个数一个加一、一个减一,操作次数不限。
问他最多可以得到多少个相同的数。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据占两行,第一行输入一个n代表元素个数,下面一行输入n个整数a[]。
注:1 <= t <= 30,1 <= n <= 1e4,1 <= a[] <= 1e3。

Output

输出一个整数代表最多可以得到多少个相同的数。

Sample Input

2
3
1 1 1
2
2 3

Sample Output

3
1

HINT

题解:

如果n个数之和是n的倍数--那么一定可以变成n个相同的数--

如果不能---(贪心)可以让一个数取极值--那么剩下的n-1个数就能变成相同的--

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define LL long longint shu[42000],n;int main() {    int t,s;scanf("%d",&t);    while (t--)    {        scanf("%d",&n);s=0;        for (int i=0;i<n;i++)        {            scanf("%d",&shu[i]);            s+=shu[i];        }        if (s%n==0)        printf("%d\n",n);        else        printf("%d\n",n-1);    }    return 0;}/**************************************************************    Problem: 1898    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:19 ms    Memory:1036 kb****************************************************************/


Problem H: 985的最大和难题

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 216  Solved: 11

SubmitStatusWeb Board

Description

985有2 * n - 1个整数,他每次可以将其中n个数变号,操作次数不限,问他可以得到的最大和。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据占两行,第一行输入一个整数n,下面一行输入2*n-1个整数a[]。
注:1 <= t <= 32,1 <= n <= 1e3,-1e3 <= a[] <= 1e3。

Output

输出一个整数代表可以得到的最大和。

Sample Input

2
2
1 1 1
2
-10 20 -10

Sample Output

340

HINT

设负数的个数为p,交换的次数为y,全体都变有x次

可列出:

扩展欧几里德:

x(2n-1)+ y*n=1

进而得:

X*P*(2N-1)+Y*P*N=P

1》如果x*p为偶数--说明都换位了非负数==只需求所有数的绝对值之和

2》如果x*p为奇数--就是x, p都为奇数的情况下--

p个负数变成了正数,,但是所有的正数都又i经过x*p次换号--都变为了非正数--

在这种情况下我们只能的到这个方程:


(X*P-1)*(2N-1)+(Y*P-1)*N=P-1

或这个方程:(X*P+1)*(2N-1)+(Y*P+1)*N=P+1

这样就只有一个是非整数---

我们就求出所有数的绝对值之和减去2*最小的绝对值(让它为非正数)

代码:

#include<cstdio>#include<cstring>#include<string>#include<algorithm>using namespace std;#define LL long longint extend(int a,int b,int &x,int &y){    if (b==0)    {        x=1;y=0;        return a;    }    else{        int t=extend(b,a%b,y,x);        y-=(a/b*x);        return t;    }}int shu[1020];int main(){    int t;scanf("%d",&t);    while (t--)    {        int n;scanf("%d",&n);        int s=0,a,p=2*n-1,zheng,fu=0;        for (int i=0;i<p;i++)        {            scanf("%d",&shu[i]);            if (shu[i]<0)                fu++;        }        int x,y;        extend(p,n,x,y);        x=(x%n+n)%n;        if (fu%2&&x%2)        {            int mi=100000;            for (int i=0;i<p;i++)            {                shu[i]=abs(shu[i]);                s+=shu[i];            }            sort(shu,shu+p);            s-=shu[0]*2;        }        else        {            for (int i=0;i<p;i++)            s+=abs(shu[i]);        }        printf("%d\n",s);    }    return 0;} /**************************************************************    Problem: 1899    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:1 ms    Memory:880 kb****************************************************************/


Problem I: 985的“树”难题

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 24  Solved: 4

SubmitStatusWeb Board

Description

985给你一棵“树”以及它的根节点,要求你先判定它是否是一棵树,其次他想知道每个节点的“太子”数目以及它的父亲(root的话输出自己)。
“太子判定条件”:
一、若x是y的孩子节点,那么x是y的“太子”;
二、若x是y的“太子”且y是z的“太子”,那么x是z的“太子”。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行输入两个整数n,root分别代表树的节点数目以及根节点的编号。
接下来n-1行,每行输出两个整数u,v代表u节点和v节点之间有一条树边。
注:1 <= t <= 20,1 <= n <= 1e4,1 <= root <= n,1 <= u,v <= n。

Output

对每一组数据,若给定的“树”不合法输出NO即可,反之输出YES,接下来输出占两行:
第一行输出n个整数代表每个节点的“太子”数目,
第二行输出n个整数代表每个节点的父亲节点编号。
输出顺序从1到n,每两个数之间有一个空格,最后一个数后面没有空格。

Sample Input

23 11 22 32 11 1

Sample Output

YES2 1 01 1 2NO

HINT

比赛的时候看成了求父亲节点的数目---然后memset(fafe,true,sizeof(fafe));这个地方还写成了memset(fafe,true,sizeof(boot));

回溯法求子节点的数目----LCA中的原理--

wrong的好伤心呀---大哭大哭


代码:

#include<cstdio>#include<cstring>#include<string>#include<algorithm>using namespace std;#define LL long long#define MA 40000struct node{int to,next;}bian[MA];int head[MA];bool fafe[MA];void DFS(int xx){int lp;for (int j=head[xx];j!=-1;j=bian[j].next){lp=bian[j].to;if (fafe[lp]){fafe[lp]=false;DFS(lp);}}}int vis[MA*100];int D[MA],kp;int ans[MA],wei[MA],ii,fer[MA];void xun(int xx,int de){D[xx]=de;vis[kp++]=xx;for (int j=head[xx];j!=-1;j=bian[j].next){int lp=bian[j].to;if (fafe[lp])        {            fafe[lp]=false;            fer[lp]=xx;             xun(lp,de+1);            vis[kp++]=xx;        }}return ;}int main(){int t;scanf("%d",&t);while (t--){int n,boot;scanf("%d%d",&n,&boot);for (int i=1;i<=n;i++)head[i]=-1;int a,b;for (int i=0;i<n-1;i++){scanf("%d%d",&a,&b);bian[2*i].to=a;bian[i*2].next=head[b];head[b]=2*i;bian[2*i+1].to=b;bian[i*2+1].next=head[a];head[a]=2*i+1;}memset(fafe,true,sizeof(fafe));fafe[boot]=false;DFS(boot);bool falg=true;for (int i=1;i<=n;i++){if (fafe[i]){falg=false;break;}}if (!falg){printf("NO\n");continue;}memset(D,0,sizeof(D));kp=1;memset(fafe,true,sizeof(fafe));fafe[boot]=false;xun(boot,1);//错误了--memset(ans,0,sizeof(ans));memset(wei,0,sizeof(wei));for (int i=1;i<kp;i++){ii=vis[i];if (!wei[ii])wei[ii]=i;else{ans[ii]+=i-wei[ii];wei[ii]=i;}}for (int i=1;i<=n;i++)ans[i]/=2;printf("YES\n");for (int i=1;i<n;i++)printf("%d ",ans[i]);printf("%d\n",ans[n]);for (int i=1;i<=n;i++)D[i]--;D[boot]=1;fer[boot]=boot;for (int i=1;i<n;i++)printf("%d ",fer[i]);printf("%d\n",fer[n]);}return 0;}


Problem J: 985的SS串难题

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 37  Solved: 4

SubmitStatusWeb Board

Description

985定义一个串是SS串的条件是:串中不同字符个数为平方数或者平方数的2倍。现在他给你一个字符串,要求你按字典序输出所有不相同的SS子串。
(1 4  9 16 25 36...)
(2 8 18 32 50 72...)

Input

第一行输入一个整数t,代表有t组测试数据。每组数据输入一个字符串str。
注:1 <= t <= 10,1 <= |str| <= 1000,保证str里面只有小写字母。

Output

按字典序输出所有不相同的SS串。

Sample Input

1
abc

Sample Output

a
ab
b
bc
c

HINT

字典树水过(比赛时内存释放应该是出了一些情况---所以我开了12个boot(字典树的根))

然后就是存入单词--输出单词---

用字典树输出字典序好完美-.-

代码:

#include<cstdio>#include<string>#include<vector>#include<cstring>#include<algorithm>using namespace std;#define LL long longbool fafe[26];int dui[10]={1,4,9,16,25,2,8,18};int sl(int xx){    for (int i=0;i<8;i++)    if (xx==dui[i])    return true;    return false;}struct node{    struct node * chi[26];    char cha[1050];    int fafa;}boot[12];//struct node *boot[12] = new node();void jia(char xx[],int lp){    node * kk=&boot[lp];    int ll=strlen(xx),k;    for (int i=0;i<ll;i++)    {        k=xx[i]-'a';        if (!kk->chi[k])            kk->chi[k]=new node();        kk=kk->chi[k];    }    strcpy(kk->cha,xx);    kk->fafa=1;}void prin(node * xx){    if (xx->fafa)        printf("%s\n",xx->cha);    for (int i=0;i<26;i++)    {        if (xx->chi[i])        prin(xx->chi[i]);    }}void booop(node * xx){    for (int i=0;i<26;i++)        if (xx->chi[i])        booop(xx->chi[i]);    delete xx;}int main(){    int t,s,kp;scanf("%d",&t);    char ch[1050],pp[1050];    int ca=0;    while (t--)    {        scanf("%s",ch);        int n=strlen(ch);//printf("%d   66\n",n);        for (int i=0;i<n;i++)        {             memset(fafe,false,sizeof(fafe));            s=0;            for (int j=i;j<n;j++)            {                 int p=ch[j]-'a';                if (!fafe[p])                {                    fafe[p]=true;                    s++;                }                if (sl(s))                {                    kp=0;                    for (int k=i;k<=j;k++)                    pp[kp++]=ch[k];                    pp[kp]=0;                    jia(pp,ca);                }            }        }         for (int i=0;i<26;i++)        {            if (boot[ca].chi[i])            prin(boot[ca].chi[i]);        }        for (int i=0;i<26;i++)        {            if (boot[ca].chi[i])            booop(boot[ca].chi[i]);        }        ca++;    }    return 0;} /**************************************************************    Problem: 1901    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:837 ms    Memory:88400 kb****************************************************************/


Problem K: 985的因子对难题

Time Limit: 2 Sec  Memory Limit: 128 MB
Submit: 114  Solved: 22

SubmitStatusWeb Board

Description

985有n个正整数,他想知道存在多少个不同的因子对(a[i], a[j])使得
1 <= i, j <= n && i != j && a[j] % a[i] == 0,其中i和j是元素的下标。
特别地,他认为(a[i],a[j])与(a[j],a[i])是一样的因子对。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据占两行,第一行输入一个n代表元素个数,下面一行输入n个整数a[]。
注:1 <= t <= 30,1 <= n <= 1e5,1 <= a[] <= 1e6。

Output

一个整数代表最后的答案。

Sample Input

2
5
1 2 3 4 5
5
2 2 2 2 2

Sample Output

5
10

HINT

优化:相同数合并--再用类似素数打表的O(nlogn)的计算思路--挤了过去

代码:

#include<cstdio>#include<cstring>#include<string>#include<algorithm>using namespace std;#define LL long longint n,shu[1001000],pp[1001000];int main() {    int t;scanf("%d",&t);    while (t--)    {        scanf("%d",&n);int a;        memset(shu,0,sizeof(shu));        memset(pp,0,sizeof(pp));        for (int i=0;i<n;i++)        {            scanf("%d",&a);            shu[a]++;        }        for (int i=1;i<=1000000;i++)        {            if (shu[i])            {                for (int j=2*i;j<=1000000;j+=i)                pp[j]+=shu[i];            }        }        LL s=0;        for (int i=1;i<=1000000;i++)        {            if (shu[i])            {            //  printf("%d  %d   %d 66\n",i,shu[i],pp[i]);                if (shu[i]>1)                s+=(shu[i]*(shu[i]-1))/2;                s+=pp[i]*shu[i];        //      printf("%lld\n",s);            }        }        printf("%lld\n",s);    }    return 0;}/**************************************************************    Problem: 1902    User: Leibniz_Zhang    Language: C++    Result: Accepted    Time:1252 ms    Memory:8692 kb****************************************************************/




X*P*(2N-1)+Y*P*N=P
0 0
原创粉丝点击