BestCoder Round #65
2015-12-06 17:04
309 查看
ZYB's Biology
Time Limit: 2000/1000 MS (Java/Others)Memory Limit: 131072/131072 K (Java/Others)
问题描述
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 30 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; }
相关文章推荐
- C++学习笔记33——友元
- Bootstrap中六种表格样式
- 网页设计的一些技巧
- Kmeans、Kmeans++和KNN算法比较
- 通过左右滑动缩放图片
- 黑苹果安装傻瓜教程
- Gradle多渠道打包 (1)
- Windows下LM-Hash与NTLM-Hash生成原理
- LIGHTOJ-1148 - Mad Counting(规律)
- zzuli OJ 1124: 两个有序数组合并
- emacs 安装及配置
- vuejs学习资料
- C-049.宏定义#define
- 【JavaScript】(5)——JavaScript与cookie、XML交互
- C 标准库 strcspn 函数的实现
- 64位windows系统下安装Memcache缓存
- Arduino 平台与C语言程序设计-week1-Arduino Environment-Lesson2
- 文章标题
- 图论入门(2):Dijkstra算法(POJ 2387)
- Java 学习笔记(一)