BestCoder Round #65

ZYB's Biology

 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYB(ZJ-267)ZYB(ZJ267)NOIPNOIP拿到600600分之后开始虐生物题,他现在扔给你一道简单的生物题:给出一个DNADNA序列和一个RNARNA序列,
问它们是否配对。

DNADNA序列是仅由A,C,G,TA,C,G,T组成的字符串,RNARNA序列是仅由A,C,G,UA,C,G,U组成的字符串。

DNADNARNARNA匹配当且仅当每个位置上AAUU,TTAA,CCGG,GGCC匹配。
输入描述
第一行一个整数TT表示数据组数。

对于每组数据:

第一行一个整数NN表示DNADNARNARNA序列的长度.

第二行一个长度为NN的字符串AA表示DNADNA序列.

第三行一个长度为NN的字符串BB表示RNARNA序列.

1 \leq T \leq 101T10,1 \leq N \leq 1001N100
输出描述
对于每组数据,输出一行YESYESNONO,表示是否匹配.
输入样例
2
4
ACGT
UGCA
4
ACGT
ACGU
输出样例
YES
NO
题解:这是一道签到题,首先先判断输入的序列分别是不是RNA序列和DNA序列,然后map映射一下,判断是否匹配
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include<map>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include<set>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
map<int,int>mp;
char s1[110],s2[110];

bool check1(char c){
    if(c=='T'||c=='A'||c=='C'||c=='G')
        return true;
    return false;
}

bool check2(char c){
    if(c=='U'||c=='A'||c=='C'||c=='G')
        return true;
    return false;
}

int main(){
    mp['A'-'A']='U'-'A';
    mp['T'-'A']='A'-'A';
    mp['C'-'A']='G'-'A';
    mp['G'-'A']='C'-'A';
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        scanf("%s%s",s1,s2);
        int flag=1;
        for(int i=0;i<n;i++){
            if(!check1(s1[i]))
                flag=0;
            if(!check2(s2[i]))
                flag=0;
            if(mp[s1[i]-'A']!=(s2[i]-'A'))
                flag=0;
        }
        if(flag==0)
            printf("NO\n");
        else
            printf("YES\n");
    }
    return 0;
}


   
   
   
   

ZYB's Game

 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
ZYBZYB在远足中,和同学们玩了一个“数字炸弹”游戏:由主持人心里想一个在[1,N][1,N]中的数字XX,然后玩家们轮流猜一个数字,如果一个玩家恰好猜中XX则算负,否则主持人将告诉全场的人当前的数和XX比是偏大还是偏小,然后猜测的范围就会相应减小,一开始的范围是[1,N][1,N].每个玩家只能在合法的范围中猜测.

现在假设只有两个人在玩这个游戏,并且两个人都已经知道了最后的XX,若两个人都采取最优策略.求X \in [1,N]X[1,N]中是后手胜利的XX数量.
输入描述
第一行一个整数TT表示数据组数。

接下来TT行,每行一个正整数NN.

1 \leq T \leq 1000001T100000,1 \leq N \leq 100000001N10000000
输出描述
TT行每行一个整数表示答案.
输入样例
1
3
输出样例
1
题解:可以手动模拟一下每个数的答案,发现如果n为偶数的时候,后者是必败的,假设实际的数是x小于等于n/2,第一个先取n-n/2+1,那么这时候这个数能取的左侧和右侧是相同的,如果后者去一个数y,先者只要在x的对立面取一个数(与y到x的距离相同的数),x大于n/2同理
若n为奇数,同理只有在x=(n+1)/2的时候才能取胜
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include<map>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include<set>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;

int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        if(n%2==0)
            printf("0\n");
        else
            printf("1\n");
    }
    return 0;
}



   
   
   
   

ZYB's Premutation

 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYBZYB有一个排列PP,但他只记得PP中每个前缀区间的逆序对数,现在他要求你还原这个排列.

(i,j)(i < j)(i,j)(i<j)被称为一对逆序对当且仅当A_i>A_jAi>Aj
输入描述
第一行一个整数TT表示数据组数。

接下来每组数据:

第一行一个正整数NN,描述排列的长度.

第二行NN个正整数A_iAi,描述前缀区间[1,i][1,i]的逆序对数.

数据保证合法.

1 \leq T \leq 51T5,1 \leq N \leq 500001N50000
输出描述
TT行每行NN个整数表示答案的排列.
输入样例
1
3
0 1 2
输出样例
3 1 2
方法一:
根据前缀和可以求出每个位置i和前面的数形成的逆序数对,记为b[i],从后往前处理,每处理一个位置i,把这个位置的数插入到树状树状中,那么问题的关键又变成了怎么确定当前位置的值,分析可值,当前这个位置的值最大为n-b[i],最小为1,那么就二分这个位置的值是多少,即low=1,high=n-b[i];
有树状树状的前缀和可以知道在这个位置后面 小于等于这个数的值出现了sum(mid)个,所以后面比mid这个值大的数有n-i-sum(mid)个,前面又有b[i]个比他大,所以如果比mid大的一共有n-i-sum(mid)+b[i]个,记为y个,如果n-mid>=y(左边的意思是在1-n中有n-mid个数比mid大),这时说明mid可以变得更大
(如果n-mid==y的时候,如果flag[mid]==1,表示出现过了,那么实际的这个位置的值一定还要小,解释看代码),否则表示这个位置的最大值也不能取到mid,二分出来的答案就是这个位置的值,所以总的时间复杂度就是n*logn*logn
#include<bits/stdc++.h>
#define mem0(a) memset(a,0,sizeof(a))
int a[50010],b[50010],C[50010],num[50010],flag[50010],n;

int lowbit(int x){
    return x&(-x);
}

int sum(int i){
    int s=0;
    while(i>0){
        s+=C[i];
        i-=lowbit(i);
    }
    return s;
}

void add(int i){
    while(i<=n){
        C[i]++;
        i+=lowbit(i);
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        mem0(C);
        mem0(flag);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=2;i<=n;i++)
            b[i]=a[i]-a[i-1];//表示这一位产生的逆序数对
        for(int i=n;i>=2;i--){
            if(i==n){
                num[i]=n-b[i];
                //printf("%d\n",num[i]);
                flag[num[i]]=1;
                add(num[i]);
            }
            else{
                int high=n-b[i];//最多为多少
                //printf("%d\n",high);
                int low=1;
                while(high-low>=0){
                    int mid=(high+low)/2;//如果为mid,sum(mid)表示为小于等于mid的出现了几个
                    int x=n-i-sum(mid)+b[i];
                    int y=n-mid;    //x,y的变化取决去mid的变化
                    if(x<=y){
                        if(x==y){
                            if(flag[mid]==1)    //如果相等,说明i的位置前面的比他大的加上后面的比他大的数的个数正好等于n-i;
                                high=mid-1;     //比如i=4,n=9,说明5,6,7,8,9一定不会是i这个位置上的数
                            else{
                                high=mid;   //只可能有一个点满足等于的情况
                                break;
                            }
                        }
                        else
                            low=mid+1;
                    }
                    else
                        high=mid-1;
                }
                num[i]=high;
                flag[num[i]]=1;
                add(num[i]);
            }
        }
        for(int i=1;i<=n;i++)
            if(flag[i]==0)
                num[1]=i;
        printf("%d",num[1]);
        for(int i=2;i<=n;i++)
            printf(" %d",num[i]);
        printf("\n");
    }
    return 0;
}

方法二:
f_ifi是第ii个前缀的逆序对数,p_ipi是第ii个位置上的数,则f_i-f_{i-1}fifi1ii前面比p_ipi大的数的个数.我们考虑倒着做,当我们处理完ii后面的数,第ii个数就是剩下的数中第f_i-f_{i-1}+1fifi1+1大的数,用线段树可以轻松地求出当前第kk个是11的位置,复杂度O(N \log N)O(NlogN).
#include<bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int a[500100],b[500100],num[500100],ans;
int sumv[4*500100];

void push(int rt){
    sumv[rt]=sumv[rt<<1]+sumv[rt<<1|1];
}

void build(int l,int r,int rt){
    sumv[rt]=1;
    if(l==r)
        return ;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    push(rt);
}

void query(int l,int r,int rt,int k){
    if(l==r){
        sumv[rt]--;
        ans=l;
        push(rt);
        return ;
    }
    int m=(l+r)>>1;
    if(k<=sumv[rt*2])
        query(lson,k);
    else
        query(rson,k-sumv[rt*2]);
    push(rt);
}

int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        b[1]=0;
        for(int i=2;i<=n;i++)
            b[i]=a[i]-a[i-1];//表示这一位产生的逆序数对
        build(1,n,1);
        for(int i=n;i>=1;i--){
            int k=i-b[i];   //剩下来的数中第几小的
            query(1,n,1,k);
            num[i]=ans;
        }
        printf("%d",num[1]);
        for(int i=2;i<=n;i++)
            printf(" %d",num[i]);
        printf("\n");
    }
    return 0;
}



   
   
   
   

ZYB's Tree

 Time Limit: 3000/1500 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYBZYB有一颗NN个节点的树,现在他希望你对于每一个点,求出离每个点距离不超过KK的点的个数.

两个点(x,y)(x,y)在树上的距离定义为两个点树上最短路径经过的边数,

为了节约读入和输出的时间,我们采用如下方式进行读入输出:

读入:读入两个数A,BA,B,令fa_ifai为节点ii的父亲,fa_1=0fa1=0;fa_i=(A*i+B)\%(i-1)+1fai=(Ai+B)%(i1)+1 i \in [2,N]i[2,N] .

输出:输出时只需输出NN个点的答案的xorxor和即可。
输入描述
第一行一个整数TT表示数据组数。

接下来每组数据:

 一行四个正整数N,K,A,BN,K,A,B.

 最终数据中只有两组N \geq 100000N1000001 \leq T \leq 51T5,1 \leq N \leq 5000001N500000,1 \leq K \leq 101K10,1 \leq A,B \leq 10000001A,B1000000
输出描述
TT行每行一个整数表示答案.
输入样例
1
3 1 1 1
输出样例
3
题解:
看到k的范围便可以知道是树形dp,两次dfs即可
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include<map>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include<set>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
const int maxn=500000+100;
int head[maxn],tot;
struct Edge{
    int to,next;
}e[maxn];
int count1[maxn][11];
int k;
int count2[maxn];

void init(){
    tot=0;
    mem1(head);
}

void addedge(int u,int v){
    e[tot].to=v;
    e[tot].next=head[u];
    head[u]=tot++;
}

void dfs1(int u){
    count1[u][0]=1;
    count2[u]=0;
    for(int i=1;i<=10;i++)
        count1[u][i]=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        dfs1(v);
        for(int j=1;j<=10;j++)
            count1[u][j]+=count1[v][j-1];
    }
    for(int j=0;j<=k;j++)
        count2[u]+=count1[u][j];
}

void dfs2(int u){
    if(u!=1){
        count2[u]=0;
        for(int i=0;i<=k;i++)
            count2[u]+=count1[u][i];
    }
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        int tmp[11];
        for(int j=2;j<=10;j++)
            tmp[j]=(count1[u][j-1]-count1[v][j-2]);
        count1[v][1]++;
        count1[v][0]=1;
        for(int j=2;j<=10;j++)
            count1[v][j]+=tmp[j];
        dfs2(v);
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        mem0(count2);
        init();
        int n;
        __int64 a,b;
        scanf("%d%d%I64d%I64d",&n,&k,&a,&b);
        for(int i=2;i<=n;i++){
            int x=(a*i+b)%(i-1)+1;
            //printf("PPPP%d\n",x);
            addedge(x,i);
        }
        dfs1(1);
        //for(int i=1;i<=n;i++)
            //printf("%d\n",count2[i]);
        dfs2(1);
        //for(int i=1;i<=n;i++)
            //printf("%d\n",count2[i]);
        int ans=0;
        for(int i=1;i<=n;i++)
            ans=ans^count2[i];
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(BestCoder Round #65)