ZYB's Biology
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYB(ZJ−267)在NOIP拿到600分之后开始虐生物题,他现在扔给你一道简单的生物题:给出一个DNA序列和一个RNA序列, 问它们是否配对。 DNA序列是仅由A,C,G,T组成的字符串,RNA序列是仅由A,C,G,U组成的字符串。 DNA和RNA匹配当且仅当每个位置上A与U,T与A,C与G,G与C匹配。
输入描述
第一行一个整数T表示数据组数。 对于每组数据: 第一行一个整数N表示DNA和RNA序列的长度. 第二行一个长度为N的字符串A表示DNA序列. 第三行一个长度为N的字符串B表示RNA序列. 1≤T≤10,1≤N≤100
输出描述
对于每组数据,输出一行YES或NO,表示是否匹配.
输入样例
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)题解:可以手动模拟一下每个数的答案,发现如果n为偶数的时候,后者是必败的,假设实际的数是x小于等于n/2,第一个先取n-n/2+1,那么这时候这个数能取的左侧和右侧是相同的,如果后者去一个数y,先者只要在x的对立面取一个数(与y到x的距离相同的数),x大于n/2同理问题描述ZYB在远足中,和同学们玩了一个“数字炸弹”游戏:由主持人心里想一个在[1,N]中的数字X,然后玩家们轮流猜一个数字,如果一个玩家恰好猜中X则算负,否则主持人将告诉全场的人当前的数和X比是偏大还是偏小,然后猜测的范围就会相应减小,一开始的范围是[1,N].每个玩家只能在合法的范围中猜测. 现在假设只有两个人在玩这个游戏,并且两个人都已经知道了最后的X,若两个人都采取最优策略.求X∈[1,N]中是后手胜利的X数量.输入描述第一行一个整数T表示数据组数。 接下来T行,每行一个正整数N. 1≤T≤100000,1≤N≤10000000输出描述T行每行一个整数表示答案.
输入样例1 3输出样例1
若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)方法一:问题描述ZYB有一个排列P,但他只记得P中每个前缀区间的逆序对数,现在他要求你还原这个排列. (i,j)(i<j)被称为一对逆序对当且仅当Ai>Aj输入描述第一行一个整数T表示数据组数。 接下来每组数据: 第一行一个正整数N,描述排列的长度. 第二行N个正整数Ai,描述前缀区间[1,i]的逆序对数. 数据保证合法. 1≤T≤5,1≤N≤50000输出描述T行每行N个整数表示答案的排列.输入样例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; }
方法二:
设fi是第i个前缀的逆序对数,pi是第i个位置上的数,则fi−fi−1是i前面比pi大的数的个数.我们考虑倒着做,当我们处理完i后面的数,第i个数就是剩下的数中第fi−fi−1+1大的数,用线段树可以轻松地求出当前第k个是1的位置,复杂度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)题解:问题描述ZYB有一颗N个节点的树,现在他希望你对于每一个点,求出离每个点距离不超过K的点的个数. 两个点(x,y)在树上的距离定义为两个点树上最短路径经过的边数, 为了节约读入和输出的时间,我们采用如下方式进行读入输出: 读入:读入两个数A,B,令fai为节点i的父亲,fa1=0;fai=(A∗i+B)%(i−1)+1 i∈[2,N] . 输出:输出时只需输出N个点的答案的xor和即可。输入描述第一行一个整数T表示数据组数。 接下来每组数据: 一行四个正整数N,K,A,B. 最终数据中只有两组N≥100000。 1≤T≤5,1≤N≤500000,1≤K≤10,1≤A,B≤1000000输出描述T行每行一个整数表示答案.
输入样例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; }