模拟题:桌球 , 需要非常细心,读懂每条规则 , 对于英语水平低下编程能力不高的我,是个大大的挑战!
参考代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long LL;
typedef set<int>::iterator SIT;
const int maxn = 1010 ;
set<int>ontable ;
int first[maxn] , p ;
int pock[maxn] , q;
inline int nextint() { int x; scanf("%d" ,&x) ; return x; }
int main()
{
int n , m;
while(scanf("%d%d" ,&n ,&m)==2){
int A[2] ={0} ;
int cur = 0;
ontable.clear();
for(int i=0;i<n;i++) ontable.insert(nextint());
while(m--){
p = nextint();
int target = *ontable.begin();
for(int i=0;i<p;i++) first[i] = nextint();
int max_f = 0;
bool f_tar = false;
for(int i=0; i< p ;i++) {
if(first[i] == target) f_tar =true;
max_f = max( max_f , first[i]) ;
}
int sum = 0;
q = nextint();
bool pock_tar = false;
bool pock_mu = false;
for(int i=0 ; i<q ;i++) {
pock[i] = nextint();
if(pock[i] == 0) pock_mu = true;
if(pock[i] == target) pock_tar = true;
sum += pock[i];
}
// 1.母球入袋
if(pock_mu && p==0) { A[cur^1] += target ; cur^=1; continue; } // 没打中球
if(pock_mu && p>0 ) { // 打中了球
A[cur^1] += max_f ;
for(int i= 0 ;i<q ;i++) {
if(pock[i] == 0) continue;
ontable.erase(pock[i]) ;
}
A[cur^1]+=sum;
cur^=1 ;
continue;
}
// 2.母球不入袋
if(p==0){ // 没打中球
A[cur^1] += target;
cur ^=1;
continue;
}
if(p>1) { // 第一次打中了多个球
A[cur^1] += max_f ;
//
A[cur^1]+=sum;
for(int i=0;i<q; i++) ontable.erase(pock[i]);
cur^=1 ;
continue;
}
if(p==1 && first[0]==target) { // 第一次只打中了目标球
if(q==0) { cur^=1 ; }
else if(q > 0) {
for(int i=0 ;i<q ;i++) ontable.erase(pock[i]) ;
if(pock_tar) A[cur] += sum ;
else { A[cur^1] += sum ; cur^=1 ; }
}
continue;
}
if(p==1 && first[0]!=target) { //
for(int i=0;i<q; i++) ontable.erase(pock[i]);
A[cur^1] += max_f ;
//
A[cur^1]+=sum;
cur^=1 ;
continue;
}
}
printf("%d : %d\n" , A[0] ,A[1]) ;
}
return 0;
}
题意:判断N个1组成的b进制的数是否是素数。经典的解法:Miller_Rabin 。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long LL;
typedef unsigned long long I64;
const char * pformat = "%I64d" ;
I64 big_rand(I64 m){
I64 x = rand();
x *= rand();
if(x<0) x = -x;
return x %= m;
}
I64 mod_mul(I64 x , I64 y , I64 n){
if(x==0 || y==0) return 0;
return ( ((x&1)*y)%n + (mod_mul(x>>1 , y , n)<<1)%n ) % n;
}
I64 mod_exp(I64 x, I64 y , I64 n){
I64 ret = 1;
while(y){
if(y&1) ret = mod_mul(ret , x ,n);
x = mod_mul(x , x ,n);
y>>=1;
}
return ret;
}
bool miller_rabbin(I64 n){
I64 i , j , x , m ,k;
if(n==2) return true;
if(n<2 || !(n&1)) return false;
m = n-1 ; k = 0;
while(!(m&1)) m>>=1 , k++;
for(i =0 ;i<4 ;i++){
x = big_rand(n-2) + 2;
x = mod_exp(x , m ,n);
if(x==1) continue;
for(j=0 ;j<k ;j++){
if(x== n-1) break;
x = mod_mul(x ,x ,n);
}
if(j>=k) return false;
}
return true;
}
int main()
{
//freopen("input.txt","r" ,stdin);
//freopen("output.txt","w", stdout);
ios::sync_with_stdio(0);
I64 b , n ;
while(cin>>b>>n){
I64 x = 0 , tmp = 1;
for(int i=0;i<n ;i++) {
x += tmp ;
tmp *= b;
}
if(miller_rabbin(x)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
这是到好题,深刻体会到到了建模的重要性, 赛时明知贪心找策略不太可能 , 但又想不到更好的方法,只好想贪心一直拖延到比赛结束也没想出 。将能够直接消除的两个点用边链接 , 对于每个联通分量,肯定存在生成树(这就是关键啊!!), 在有根树上只需按层数从大到小操作 , 最后肯定只剩一个树根 。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
const int maxn = 2050 ;
const char dir[][6] ={"RIGHT" ,"UP" ,"LEFT" ,"DOWN"};
struct point{int x ,y ,deep , fa; };
point P[maxn] ;
vector<int>S[maxn] ; int max_deep ;
vector<int>G[maxn] ;
vector<int>X[maxn] , Y[maxn] ;
int idx_num , idy_num;
map<int , int>idx , idy ;
int IDX(int u) {if(idx.count(u)) return idx[u] ; return idx[u]=idx_num++; }
int IDY(int u) {if(idy.count(u)) return idy[u] ; return idy[u]=idy_num++; }
bool vis[maxn] ;
void bfs(int u ,int n){
vis[u] = true;
queue<int>Q ;
Q.push(u);
while(!Q.empty()){
u = Q.front() ; Q.pop();
for(int i=0;i<(int)G[u].size();i++){
int v = G[u][i];
if(vis[v]) continue;
vis[v] = true;
P[v].deep = P[u].deep+1 ;
P[v].fa = u;
S[P[v].deep].push_back(v) ;
max_deep = max(max_deep , P[v].deep);
Q.push(v);
}
}
}
int get_dir(int u){
int fa = P[u].fa;
if(P[u].x < P[fa].x) return 0;
if(P[u].y < P[fa].y) return 1;
if(P[u].x > P[fa].x) return 2;
if(P[u].y > P[fa].y) return 3;
return -1;
}
bool cmpx(const int u ,const int v){return P[u].x < P[v].x ; }
bool cmpy(const int u ,const int v) { return P[u].y < P[v].y ;}
int main()
{
int n ;
while(scanf("%d",&n)==1){
max_deep = 0;
memset(vis , 0 ,sizeof(vis));
idx.clear() , idy.clear();
idx_num = idy_num = 0;
for(int i=0;i<n ;i++) {
S[i].clear() , G[i].clear();
X[i].clear() , Y[i].clear();
}
for(int i=0;i<n;i++){
scanf("%d%d" ,&P[i].x , &P[i].y) ;
X[IDX(P[i].x)].push_back(i);
Y[IDY(P[i].y)].push_back(i);
P[i].deep = P[i].fa = 0;
}
// 同一行的加边
for(int i=0;i<n;i++){
sort(X[i].begin(),X[i].end(),cmpy);
int vcnt = X[i].size();
for(int j=0;j<vcnt-1;j++){
G[X[i][j]].push_back(X[i][j+1]);
G[X[i][j+1]].push_back(X[i][j]);
}
}
// 同一列的加边
for(int i=0;i<n;i++){
sort(Y[i].begin(),Y[i].end(),cmpx);
int vcnt = Y[i].size();
for(int j=0;j<vcnt-1;j++){
G[Y[i][j]].push_back(Y[i][j+1]);
G[Y[i][j+1]].push_back(Y[i][j]);
}
}
int balls_left = 0;
for(int i=0;i<n;i++) if(!vis[i]) {
balls_left ++ ;
bfs(i , n) ;
}
printf("%d\n" , balls_left);
for(int d = max_deep ; d>=1 ;d--){
int vcnt = S[d].size();
for(int i=0;i<vcnt ;i++){
int u = S[d][i];
printf("(%d, %d) %s\n" , P[u].x , P[u].y , dir[get_dir(u)]) ;
}
}
}
return 0;
}
ZOJ 3765 Lights
题意:询问一个区间的gcd ,同时要求支持修改, 插入, 删除等操作。
这题正是伸展树(Splay Tree)的典型操作。但是由于伸展树的编程复杂度较高,所以难倒了不少人。
关于伸展树的介绍请参见2004年杨思雨论文《伸展树的基本操作与应用》 , 这里另外还推荐一篇博客:
http://www.cnblogs.com/kernel_hcy/archive/2010/03/17/1688360.html
为什么伸展树能够对于每种操作均有这么好的性能呢? 因为伸展树是一种自适应二叉查找树 , 它能够通过一些列操作调整自我形态 , 以保证树的高度基本平衡。 这就是其关键所在。
那么Splay Tree 是怎样调整自我形态的呢? 最重要的操作当然是强大的Splay(x)了, 其作用是在不改变元素相对逻辑顺序的前提下将X调整到树的根部 。
为了实现Splay(x) , 有三种基本的操作: Zig / Zag 、 Zig-Zig/Zag-Zag 、Zig-Zag / Zag-Zig , 而这些操作均由两个最基本的操作完成: 左旋和右旋 。
为了介绍这些基本操作 , 这里引用杨思雨的论文中的几幅图说明:
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 300000+100;
struct SplayNode{
int l , r , fa , sum ,val , flag , gcd , gcd2 ;
};
struct SplayTree{
SplayNode tree[maxn] ;
int tot , root ;
int gcd(int a, int b){
int r ;
while(b) r = a%b , a = b , b = r;
return a;
}
void init(int n){
tot = n;
memset(tree , 0 , (n+1)*sizeof(SplayNode)) ;
root = 0;
}
int nextnode(){
SplayNode & t = tree[++tot] ;
t.gcd2 = t.gcd = t.l = t.r = t.fa = t.sum = t.val = t.flag = 0;
return tot ;
}
void update(int x){
int & ans = tree[x].gcd , & ans2 = tree[x].gcd2 , & sum = tree[x].sum ;
ans2 = ans = 0 , sum = 1;
if(tree[x].l) {
sum+=tree[tree[x].l].sum ;
ans = gcd(ans , tree[tree[x].l].gcd);
ans2 = gcd(ans2 , tree[tree[x].l].gcd2);
}
if(tree[x].r) {
sum+=tree[tree[x].r].sum ;
ans = gcd(ans , tree[tree[x].r].gcd);
ans2 = gcd(ans2 , tree[tree[x].r].gcd2);
}
if(tree[x].flag) ans = gcd(ans , tree[x].val);
else ans2 = gcd(ans2 , tree[x].val);
}
void left(int now){
int fa = tree[now].fa ,gr = tree[fa].fa, lson = tree[now].l;
tree[now].fa = gr;
if(tree[gr].l==fa) tree[gr].l = now;
if(tree[gr].r==fa) tree[gr].r = now;
tree[fa].fa = now;
tree[now].l = fa;
tree[fa].r = lson ;
tree[lson].fa = fa;
update(fa);
}
void right(int now){
int fa = tree[now].fa , gr = tree[fa].fa , rson = tree[now].r;
tree[now].fa = gr;
if(tree[gr].l == fa) tree[gr].l = now;
if(tree[gr].r == fa) tree[gr].r = now;
tree[fa].fa = now;
tree[now].r = fa;
tree[rson].fa = fa;
tree[fa].l = rson;
update(fa);
}
void splay(int now , int FA = 0){
while(tree[now].fa != FA){
int fa = tree[now].fa , gr = tree[fa].fa;
if(tree[fa].l == now){
if(tree[gr].l == fa) right(fa);
right(now);
}
else {
if(tree[gr].r == fa) left(fa);
left(now);
}
}
update(now);
if(now) root = now;
}
int Find(int i , int root){
int now = root;
while(i != tree[tree[now].l].sum+1){
if(i<tree[tree[now].l].sum+1) now = tree[now].l;
else i-=tree[tree[now].l].sum+1 , now = tree[now].r;
}
splay(now);
return now;
}
int find_max(int root){
int now = root;
while(tree[now].r) now = tree[now].r;
splay(now);
return now;
}
void Insert(int i , int val , int sta){
int cur = Find(i , root) , next = nextnode();
int rson = tree[cur].r;
tree[cur].r = 0;
tree[next].val = val , tree[next].flag = sta;
tree[next].l = cur , tree[cur].fa = next;
tree[next].r = rson , tree[rson].fa = next;
update(cur);
update(next);
root = next;
}
void Delete(int x){
int cur = Find(x , root);
int lson = tree[cur].l , rson = tree[cur].r;
tree[lson].fa = tree[rson].fa = 0;
int r = find_max(lson);
tree[r].r = rson;
tree[rson].fa = r;
update(r);
root = r;
}
void Recover(int i){
int cur = Find(i , root);
tree[cur].flag = ! tree[cur].flag;
update(cur);
}
void Modify(int i, int x){
int cur = Find(i , root);
tree[cur].val = x;
update(cur);
}
int querypre(int i , int cur , int sta){
int ans = 0;
while(i > 0) {
if(i <= tree[tree[cur].l].sum ) cur = tree[cur].l;
else {
if(sta) ans = gcd(ans, tree[tree[cur].l].gcd);
else ans = gcd(ans , tree[tree[cur].l].gcd2);
if(tree[cur].flag == sta) ans = gcd(ans , tree[cur].val);
i -= tree[tree[cur].l].sum + 1;
cur = tree[cur].r;
}
}
return ans;
}
int Query(int L , int R , int sta){
int cur = Find(L , root);
int ans = 0;
if(tree[cur].flag == sta) ans = gcd(ans , tree[cur].val);
ans = gcd(ans , querypre(R-L , tree[cur].r , sta));
return ans;
}
void build(int L , int R , int fa , int type){
if(L > R) return ;
int mid = (L+R)>>1;
tree[mid].fa = fa;
if(type==0) tree[fa].l = mid;
else tree[fa].r= mid;
build(L , mid-1 , mid , 0) , build(mid+1 , R , mid , 1);
update(mid);
}
}sol;
int main()
{
//freopen("input.txt" , "r" ,stdin);
int N , Q;
while(scanf("%d%d" ,&N ,&Q)==2){
N+=2;
sol.init(N);
int val , sta , x , L , R ;
for(int i=2;i<N;i++){
scanf("%d%d" ,&val , &sta);
sol.tree[i].val = val;
sol.tree[i].flag = sta;
}
int mid = (1+N)/2;
sol.build(1,mid-1,mid,0) , sol.build(mid+1,N,mid,1);
sol.update(mid);
sol.root = mid;
char oper[5];
while(Q--){
scanf("%s",oper);
if(oper[0] == 'Q'){
scanf("%d%d%d" ,&L ,&R , &sta); L++ , R++;
int ans = sol.Query(L ,R, sta);
if(ans ==0) ans = -1;
printf("%d\n" , ans);
}
else if(oper[0] == 'I'){
scanf("%d%d%d" ,&x , &val ,&sta); x++;
sol.Insert(x ,val , sta);
}
else if(oper[0] == 'D'){
scanf("%d" ,&x); x++ ;
sol.Delete(x);
}
else if(oper[0] == 'R'){
scanf("%d" ,&x); x++;
sol.Recover(x);
}
else if(oper[0] == 'M'){
scanf("%d%d" ,&x ,&val); x++;
sol.Modify(x , val);
}
}
}
return 0;
}