NOIP2012 简要题解

2 篇文章 0 订阅

Day1 P1 Vigenere密码

直接根据规律模拟即可

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2000;
char S[maxn],T[maxn];
int calc(char c)
{
    return (int(c)-65-(c>=97?32:0));
}
int main()
{
    scanf("%s",S);
    scanf("%s",T);
    for (int i = 0 ; T[i]!='\0'; ++i)
    {
        for (int j = 0 ; j < 26; ++j)
        {
            if ((j+calc(S[i%strlen(S)]))%26 == calc(T[i])) 
               printf("%c",j+65+(T[i]>=97?32:0));
        }
    }
    return 0;
}


Day1 P2    国王游戏

首先我们分析一下 如果i和j两个相邻那么i排在j前面的必要条件是  total * a[i] / b[j]  <  total * a[j] /b[i] 也就是说 a[i]*b[i] < a[j]*b[j]

这样就立刻确定了顺序(有序性在信息学竞赛中的应用)

接下来就是模拟出每个大臣得到的数,直接使用一下高精度计算即可。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define INF 1000000000
using namespace std;
const int maxn = 200000;
struct big{
       int a[maxn],len;
       big(){ memset(a,0,sizeof(a)); len = 1;};
       string str() const
       {
              string res = "";
              for (int i = 0 ;i<len;++i)res= char(a[i]+'0')+res;
              return res;
       }
       big operator = (const char* num)
       {
           len = strlen(num);
           for (int i = 0;i < len;++i) a[i]=num[len-i-1]-'0';
           while (!a[len-1] && len>1) len--; 
           return *this;
       }
       big operator = (int num)
       {
           char s[maxn];
           sprintf(s, "%d", num);
           *this = s;
           return *this;
       }
       big operator * (int b)
       {
           big c ;
           c.len = len;
           for (int i = 0 ; i <c.len ; ++i)
               c.a[i] =  a[i]*b;
           for (int i = 0 ; i <c.len; ++i)
           {
               c.a[i+1]+=c.a[i]/10;
               c.a[i]%=10;
           }
           while (c.a[c.len]>0) 
           {
                 c.a[c.len+1]=c.a[c.len]/10;
                 c.a[c.len]%=10;
                 c.len++;
           }
           return c;
       }
       big operator /(int b)
       {
           big  c= *this;
           int x =0;
           for (int i = len-1; i >=0; i--)
           {
               int y  = (x*10+c.a[i])%b;
               c.a[i] = (x*10+c.a[i])/b;
               x = y;
           }
           /*if (x>0)
           {
               int i =0;
               do{
                    a[i]++;
                    if (a[i]>=10) a[i+1]++,a[i]-=10;else
                    break;
               }while(true);
           }*/
           while (c.a[c.len-1]==0 && c.len>1) c.len--;
           return c;
       }
       big operator +(const big & b)
       {
           big c;
           c.len = 0 ;
           for (int i =0, g=0; g|| i< max(len, b.len);++i)
           {
               int x = g;
               if (i<len)x+=a[i];
               if (i<b.len) x+=b.a[i];
               c.a[c.len++] = x%10;
               g=x/10;
           }
           return c;
       }
       big operator +=(const big &b) 
       {
           *this = *this + b;
           return *this;
       }
       big(int num) { *this = num;}
       big(const char* num){ *this = num ;}
       
       bool operator < (const big &b)const 
       {
           if (len!=b.len) return len<b.len;
           for (int i = len-1; i>=0; i--)
           if (a[i] != b.a[i]) return a[i]<b.a[i];
           return false; 
       }
       void       print()
       {
                  for (int i = len-1 ; i>=0;i--)
                  printf("%d",a[i]);
                  printf("\n");
       }
}ans,answer,t;
struct node 
{
       int x,y;
       bool operator <(const node& a) const 
       {
            return x*y < a.x*a.y;
       }
}p[maxn];
istream& operator >> (istream& in,  big &x)
{
         string s;
         in>>s;
         x = s.c_str();
         return in;
}
ostream& operator << (ostream& out, big &x)
{
   out<< x.str();
   return out;
}
int main()
{ 
    int n ;
    cin >> n ;
    for (int i = 0; i <=n ;++i)
        scanf("%d %d",&p[i].x,&p[i].y);
    sort(p+1, p+n+1);

    ans = p[0].x;
    answer = 0;
    for (int i = 1 ; i <=n; ++i )
    {
        t = ans / p[i].y;
        if (answer<t)  answer = t;
        ans =ans * p[i].x;
        //cout<<p[i].x<<" ";
        //ans.print();
    }
    answer.print();   
    return 0;
}

Day1 P3   开车旅行

A和B交替轮流开车一个选择最近的一个选择第二近的前进。
首先A和B交替前进可以看做是A+B一个整体前进。然后最后一步特殊处理即可。

然后关于找最近的点和第二近的点,类似于找前驱和后继,C++可以中set实现,至于Pascal语言,可以考虑平衡术,但是难度太大,还是用二叉查找树。

然后利用路径上的倍增思想,f[i][j]表示i点前进2^j次步走到了多少,g[i][j]表示i节点前进2^j次步走到哪个节点。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
const int maxn = 200000+10;
typedef long long LL;

int g[maxn][20+2],next1[maxn],next2[maxn];
LL  len1[maxn],len2[maxn];
LL lenA,lenB;
int n,m,i,j;
struct    road
{
   LL A,B;
   
   road operator +(const road &a)
   {    
        road c;
        c.A = A + a.A;
        c.B = B + a.B;
        return c;
   }
}f[maxn][20+2];
struct    Point
{
    int id, h;
    bool operator<(const Point &a) const
    {
         return h < a.h;
    }
}p[maxn];
multiset<Point> S;
multiset<Point>::iterator it;
void      updata(int i ,int h,int j,int h2)
{
          if (!next1[i])
          {
              next1[i] =  j;
              len1[i]= abs(h-h2);
              return ;
          } 
          if ((abs(h-h2) == len1[i] && p[next1[i]].h > h2)
          ||  (abs(h-h2) < len1[i]))
          {
              next2[i] = next1[i];
              len2[i]  = len1[i];
              
              next1[i] = j;
              len1[i]  = abs(h-h2);      
              return ;     
          }
          if (!next2[i])
          {
              next2[i] =  j;
              len2[i]= abs(h-h2);
              return ;
          } 
          if ((abs(h-h2)== len2[i] && p[next1[i]].h > h2)
          ||  (abs(h-h2) < len2[i]))
          {
              next2[i]=  j;
              len2[i] = abs(h-h2);
          }
}
void      ask(int st,LL len)
{
          for (int j = 20; j >=0; j--)
          {
              if (g[st][j]!=0 && f[st][j].A+f[st][j].B<=len)
              {
                  len -= (f[st][j].A+f[st][j].B);
                  lenA+=f[st][j].A;
                  lenB+=f[st][j].B;
                  st = g[st][j];            
              }
          }
          if (next2[st] && len>=len2[st])
             lenA +=len2[st];
}
int main()
{
    cin>>n;
    for (int i = 1; i <= n; ++i)
    {
         scanf("%d", &p[i].h);
         p[i].id = i;
    }
    for (int i = n; i>=1; --i)
    {
        it = S.lower_bound(p[i]);
        if (it != S.end())
           updata(i , p[i].h, it->id, it->h);
        
        if (it != S.end())
        {
               it++;
               if (it != S.end())
               updata(i , p[i].h, it->id, it->h);
               it--;
        }
        
        if (it != S.begin())
        {
               it--;
               updata(i , p[i].h, it->id, it->h);
        }
        if (it != S.begin())
        {
               it--;
               updata(i , p[i].h, it->id, it->h);
        }
        S.insert(p[i]);
    }
    
    for  (int i = 1; i <= n;++i)
    {
         //cout << next2[i]<<" "<<len2[i]<<endl;
         g[i][0] = next1[next2[i]];
         f[i][0].A =  len2[i];
         f[i][0].B =  len1[next2[i]];
    }
    for (int i = 1; i <= 20; ++i)
    for (int j = 1; j <= n;++j)
    {
        g[j][i] = g[g[j][i-1]][i-1];
        /*f[j][i].A = f[j][i-1].A + f[g[j][i-1]][i-1].A;
        f[j][i].B = f[j][i-1].B + f[g[j][i-1]][i-1].B;
        if (f[2][0].A==3)
        cout<<i <<" "<<j << endl;
        */
        f[j][i]= f[j][i-1]+ f[g[j][i-1]][i-1];
    }
    LL x;
    cin>>x;
    LL ansA = (LL)round(10e9);
    LL ansB = 0;
    int Pos = 0;
    for (int i = 1; i <= n;++i)
    {
             lenA = lenB = 0;
             ask(i,x);
             //cout << lenA<<" "<<lenB<<endl;
             if (lenB != 0)
             {
                 if (Pos ==0 || ansA*lenB > lenA * ansB)     
                 {
                      Pos = i;
                      ansA = lenA;
                      ansB = lenB;
                 }
             }
    }
    cout << Pos << endl;
    cin>>m;
    for (int i = 1; i <= m; ++i)
    {
        int st;
        scanf("%d %d",&st,&x);
        lenA = lenB = 0;
        ask(st,x);
        cout<<lenA<<" "<<lenB <<endl;
    }
    return 0;
}



Day2 P1   同余方程 详见前面文章的两种解法(除了扩展欧几里得和分块外,还可以用费马定理做)。

Day2 P2    借教室
 
首先本题就是整体减一个值,看看数组里有木有值被减成了负数,这个可以用线段树维护最小值即可,看数组中的最小值是否为负数。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
const int maxn = 1000000+10;

int a[maxn];
int n,m,s,t,need;
struct node
{
       int Min[maxn*4];
       int dec[maxn*4];
       void   build(int a[],int l,int r,int p)
       {
              dec[p]=0;
              if (l==r)
              {
                  Min[p] = a[l];
                  return ;
              }
              int mid = (l+r) / 2;
              build(a,l,mid,p+p); 
              build(a,mid+1,r,p+p+1);
              Min[p] = min(Min[p+p],Min[p+p+1]);  
       }
       void   update(int p )
       {
              Min[p] = min(Min[p+p]-dec[p+p],Min[p]);
              Min[p] = min(Min[p+p+1]-dec[p+p+1],Min[p]);
       }
       void   push(int p)
       {
              dec[p+p]+=dec[p];
              dec[p+p+1]+=dec[p];
              dec[p]=0;
       }
       void   getDec(int l ,int r,int p, int a,int b,int need)
       {
              if (l==a && r==b)
              {
                  Min[p]-= (need+dec[p]);
                  if (l!=r) dec[p+p]+=dec[p]+need,dec[p+p+1]+=dec[p]+need;
                  dec[p]=0;
                  return ;
              }
              push(p);
              int mid = (l+r) / 2;
              if (b <= mid ) getDec(l,mid,p+p,a,b,need); else
              if (a >  mid ) getDec(mid+1,r,p+p+1,a,b,need); else
              {
                 getDec(l, mid ,p+p,a,mid,need);
                 getDec(mid+1,r,p+p+1,mid+1,b,need);   
              } 
              update(p);
       }
       
}tree;
int main()
{
    cin >> n >> m ;
    for (int i = 1; i <= n ;++i ) scanf("%d",&a[i]);
    tree.build(a,1,n,1);
    
    for (int i = 1; i <= m ;++i)
    {
        scanf("%d %d %d",&need, &s,&t);
        tree.getDec(1,n,1,s,t,need);
        if (tree.Min[1] < 0 )
        {
            printf("%d\n%d\n",-1,i);
            return 0;
        }
    }
    cout << 0 << endl;
    return 0;
}

 Day2 P3  疫情控制
题目大意即所有军队拦截住1节点通往叶节点的道路。

首先答案最小,可以用二分转化为判定性问题。

第二继续利用倍增思想判断军队能否达到1节点。能则记录还有多少时间可以移动,不能则停留在最上面的节点上。
然后根据目前情况DFS出1的那几个子节点需要军队,然后就是军队与子节点的一一匹配。
这个匹配可以用贪心,首先而比边的权值排序从大到小操作,对于1的某个自己点,如果有军队从他走到了1,但是回不到自身了,则
让该军队控制这个节点,否则选出还有时间能够走到该节点的剩余时间最小的军队去控制这个节点(可以用一个set,方便查找)。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
#include <map>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100000;
typedef long long LL;
multiset<int> S;
multiset<int>::iterator it;
int n,u,v,w,m,nownode;
LL f[maxn][20];
int y[maxn];
int g[maxn][20];
int sonTree[maxn];
int cnt[maxn];
int p[maxn];
int Min[maxn]; 
bool Leaf[maxn];
bool son[maxn];
struct    Arm
{
          int opt,time;
}A[maxn]; 
struct    Graph
{
     int node[maxn * 2], len[maxn * 2], next[maxn * 2], v[maxn * 2];
     int en;
     Graph(){ 
        memset(v,0,sizeof(v));
        memset(next,0, sizeof(next));
        memset(len,0,sizeof(len));  
        en  = 0; 
     }
     void   addEdge(int a,int b,int c)
     {
            en++; node[en] = b; len[en] = c; next[en] = v[a]; v[a] = en;
            en++; node[en] = a; len[en] = c; next[en] = v[b]; v[b] = en;
     }
}G;
void     DFS(int x,int father)
{
         sonTree[x] = nownode;
         bool flag= true;
         for (int j = G.v[x]; j; j = G.next[j])
         {
             if (G.node[j] != father)
             {
                 //father[j] = x;
                 flag = false;
                 f[G.node[j]][0] = G.len[j];
                 g[G.node[j]][0] = x;
                 if (x==1) nownode = G.node[j];
                 DFS(G.node[j],x); 
             }
         }
         Leaf[x] = flag;
}
void     find_leaf(int x, int father)
{
         if (cnt[x]>0) return ;
         if (Leaf[x])
         {
             son[nownode] = true;
             return;
         }
         for (int j = G.v[x]; j ; j = G.next[j])
         {
             if (G.node[j] != father)
             {
                 if (x==1) nownode = G.node[j];
                 find_leaf(G.node[j], x);
             }
         }
}
bool     ok(LL value)
{
    S.clear();
    int arm = 0;
    memset(cnt,0,sizeof(cnt));
    memset(son,0,sizeof(son));
    for (int i = 1; i <= m ;++i)
    {
        int len = value;
        int Pos = p[i];
        for (int j = 16; j>=0; --j)
            {
                 if (f[Pos][j]<=len && g[Pos][j]!=0)
                 {
                     len-=f[Pos][j];
                     Pos =g[Pos][j];              
                 }
            }
        if (Pos == 1)
        {
            A[arm].opt = p[i];
            A[arm++].time  = len;
        } else
          cnt[Pos]++;
    }           
    find_leaf(1,0); 
    for (int i = 1;i <= n;++i) Min[i]= -1;
    for (int i = 0; i < arm ; ++ i)
    {
        if (son[sonTree[A[i].opt]])
        {
           if  (Min[sonTree[A[i].opt]] ==-1 || 
              A[Min[sonTree[A[i].opt]]].time > A[i].time)
                Min[sonTree[A[i].opt]] = i;
        }
    } 
    int tot = 0;
    for (int i = 1; i <= n;++i)
    {
        if (son[i] && Min[i] != -1 && A[Min[i]].time<f[i][0])
        {
            A[Min[i]].time = -1;       
        }else
        if (son[i]) y[tot++] = f[i][0];
    }
    sort(y,y+tot);
    for (int i = 0; i < arm;++i)
    if (A[i].time != -1)
        S.insert(A[i].time);
        
    for (int i = tot-1; i>=0; --i)
    {
        if (S.lower_bound(y[i]) == S.end()) return false;
        it = S.lower_bound(y[i]);
        S.erase(it);
    }
    return true;
}
int main()
{
    cin>>n;
    for (int i = 1;i < n;++ i)
    {
        scanf("%d %d %d",&u,&v,&w);
        G.addEdge(u,v,w);
    }
    cin>>m ;
    for (int i = 1;i <= m;++i)
    {
        scanf("%d", &p[i]);
        // cnt[p[i]] ++;
    }
    DFS(1,0);
    /*for (int i = 1;i <= n; ++i)
    {
        cout <<i<<" "<<f[i][0]<<" "<<g[i][0] << endl; 
    }*/
    for (int i = 1; i <=16; ++i)
        for (int j = 1; j <= n ;++j)
        {
            f[j][i] = f[j][i-1]+ f[g[j][i-1]][i-1];
            g[j][i] = g[g[j][i-1]][i-1];
        }
    
    
    LL L = 0 , R = (LL)n * round(10e9);
    int ans = -1;
    while (L <= R)
    {
          LL mid = (L+R) /2;
          if (ok(mid)) R = mid-1, ans = mid; 
          else L = mid+1;
    }
    cout << ans << endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值