浙大月赛 ZOJ Monthly, March 2014(简单题的题解)

AAlice and Bob and Cue Sports

模拟题:桌球 , 需要非常细心,读懂每条规则 , 对于英语水平低下编程能力不高的我,是个大大的挑战!

参考代码:

#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;
}

BSingles' Day

题意:判断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;
}


EEasy billiards

这是到好题,深刻体会到到了建模的重要性, 赛时明知贪心找策略不太可能 , 但又想不到更好的方法,只好想贪心一直拖延到比赛结束也没想出 。将能够直接消除的两个点用边链接  , 对于每个联通分量,肯定存在生成树(这就是关键啊!!), 在有根树上只需按层数从大到小操作 , 最后肯定只剩一个树根 。

参考代码:

#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;
}


深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值