问题描述
ZYB(ZJ-267)ZYB(ZJ−267)在NOIPNOIP拿到600600分之后开始虐生物题,他现在扔给你一道简单的生物题:给出一个DNADNA序列和一个RNARNA序列,
问它们是否配对。
DNADNA序列是仅由A,C,G,TA,C,G,T组成的字符串,RNARNA序列是仅由A,C,G,UA,C,G,U组成的字符串。
DNADNA和RNARNA匹配当且仅当每个位置上AA与UU,TT与AA,CC与GG,GG与CC匹配。
输入描述
第一行一个整数TT表示数据组数。
对于每组数据:
第一行一个整数NN表示DNADNA和RNARNA序列的长度.
第二行一个长度为NN的字符串AA表示DNADNA序列.
第三行一个长度为NN的字符串BB表示RNARNA序列.
1 \leq T \leq 101≤T≤10,1 \leq N \leq 1001≤N≤100
输出描述
对于每组数据,输出一行YESYES或NONO,表示是否匹配.
输入样例
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 1000001≤T≤100000,1 \leq N \leq 100000001≤N≤10000000
输出描述
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 51≤T≤5,1 \leq N \leq 500001≤N≤50000
输出描述
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}fi−fi−1是ii前面比p_ipi大的数的个数.我们考虑倒着做,当我们处理完ii后面的数,第ii个数就是剩下的数中第f_i-f_{i-1}+1fi−fi−1+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=(A∗i+B)%(i−1)+1 i \in [2,N]i∈[2,N] .
输出:输出时只需输出NN个点的答案的xorxor和即可。
输入描述
第一行一个整数TT表示数据组数。
接下来每组数据:
一行四个正整数N,K,A,BN,K,A,B.
最终数据中只有两组N \geq 100000N≥100000。
1 \leq T \leq 51≤T≤5,1 \leq N \leq 5000001≤N≤500000,1 \leq K \leq 101≤K≤10,1 \leq A,B \leq 10000001≤A,B≤1000000
输出描述
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;
}