NOIP 膜你題 DAY2

NOIp膜你題   Day2node

duliu 出題人:ZAY   ios

 

 題解c++

這就是一道組合數問題鴨!!!  但是泥爲何沒有推出式子!!數組

 

首先咱們知道的是 m 盆花都要擺上,而後他們的順序不定(主人公忘記了)ui

因此初步獲得一個排列數 P( m,m ) , 即 Pmm  this

 

那麼咱們就還剩下 n-m 個空位置,這些空位置都是不能夠放花的,因而咱們逆向思惟一下:  spa

  n-m  個位置不放花,也就是能夠在這些位置周圍插空放花,把這些位置隔開,那麼就能夠把m盆花放到 n-m+1 個空裏,因爲這是對於空位置來講的,沒有順序可言,每一個都是一毛同樣的,因此獲得一個組合數  C(n-m+1 , m) , 即 Cn-m+1m  3d

 

因此 ans = P( m,m ) * C( n-m+1 , m ) 指針

               = m! * (n-m+1)! / [ m! * (n-m+1 - m)! ]code

                  化簡一下就是下面

               =(n-m+1)*(n-m+1-1)*......*(n-2m+2)

 

注意

ans 要開 long long , 試過毒了,數據不開 long long 會炸

 

代碼

#include<bits/stdc++.h>

using namespace std;

int type,n,m,p;
long long ans=1;

int main()
{
    freopen("ilove.in","r",stdin);
    freopen("ilove.out","w",stdout);
    
    scanf("%d%d%d%d",&type,&n,&m,&p);
    
    if(n==1&&m==1)  { printf("1\n");   return 0; }
    
    int a=n-m+1,b=n-m-m+2;
    for(int i=a;i>=b;i--)
      ans=(ans%p*i%p)%p;
    
    printf("%ld\n",ans);
    return 0;
    
}

 

看看出題人的題解

通常這種數數題,也就是求方案數,有兩種解決方案:DP&組合數學

1.DP

 

2.組合數學

 

Ps:插空法就是我上面題解裏面提到的

 


 

 

 

 

題解

       注意到根據題目規定的走法,在進入一個節點之後,必須遍歷完它的整個子樹, 不然一旦離開這個節點,再也沒法進入這棵子樹,從而致使該節點的某個孩子沒能放 上石子,致使這個節點不能放上石子。

     

 

       同時又有每一個節點放上石子之後,它的子樹的 石子能夠所有取回。設在節點 u 放石子須要有 ansu 個石子,則放完 u 之後能夠取回 ansu-wu 個石子。

       由於你準備了ansu個石子,u點須要wu個石子放上,那麼放完以後,他的子樹上的石子就能夠取回來了,也就是ansu-wu個,這些也就是節點u的孩子所需石子數和。

 

       因而考慮影響問題答案的顯然是從 u 進入每一個孩子的順序,因爲最多有兩個孩 子,直接比較一下就能夠知道先進入哪一個孩子更優秀了。時間複雜度 O(n)

 

 

 

 

 

 

 

代碼

 

#include <cstdio>
#include <vector>
#include <algorithm>

const int maxn = 100010;

int n;
int MU[maxn], ans[maxn];    //MU也就是W 
std::vector<int>son[maxn];

void dfs(const int u);
bool cmp(const int &_a, const int &_b);

int main() {
  freopen("yin.in", "r", stdin);
  freopen("yin.out", "w", stdout);
  
  scanf("%d", &n);
  for (int i = 2, x; i <= n; ++i) {
    scanf("%d", &x);
    son[x].push_back(i);
  }
  for (int i = 1; i <= n; ++i) {
    scanf("%d", MU + i);  
    //數組名+一個東西  這是指針的寫法
    //至關於scanf("%d",&MU[i]); 
  }
  dfs(1);
  for (int i = 1; i < n; ++i) {
    printf("%d ", ans[i]);
  }
  printf("%d\n", ans[n]);
  return 0;
}

void dfs(const int u) {
  for (auto v : son[u]) {  //遍歷u的全部子節點 
    dfs(v);
  }
  std::sort(son[u].begin(), son[u].end(), cmp);
  //按照差值不升序排序 
  int _ret = 0;
  //此時U節點的全部子節點已經算出來了,準備計算U節點 
  for (auto v : son[u]) {  //葉節點沒有兒子就不會for循環 
    if (_ret >= ans[v]) //上一個子節點收回的石子足夠擺當前節點,就擺上 
    {
      _ret -= ans[v];   //ret擺完以後更新 
    } 
    else 
    {
      ans[u] += ans[v] - _ret;  //收回的石子不夠放了,往上補 
      _ret = ans[v] - MU[v];
      //遍歷完U的兒子V節點對應的子樹後,收回除了 U的兒子v之外全部節點上的石頭 
    }
  }
  ans[u] += std::max(0, MU[u] - _ret);
  //深搜,會先到達全部葉節點,而且全部葉節點的ans=w[i] 
}

inline bool cmp(const int &_a, const int &_b) {
  return (ans[_a] - MU[_a]) > (ans[_b] - MU[_b]);
}

 

 

 

 

 


 

 

題解

 1.能夠經過前4個任務點的代碼QWQ

       由於咱們看到每封信最多隻會有30個字符對吧,那麼每次詢問給出一個詢問區間的時候,咱們就能夠比對每一位的字符

      好比咱們比對到了第 i 位,詢問區間從頭至尾枚舉看一看,對於這一位,有幾個0,幾個1,幾個?

      若是既有0又有1,那麼顯然是無解的

      若是隻有0和?,那麼這一位只能夠爲0,也就是?只能被填成0

      若是隻有1和?,同上

      若是全是?,那麼既能夠填0,又能夠填1,也就是有2種方案

 

      咱們把全部位都枚舉了一遍,那麼可能會出現結果:無解,有惟一解,有解但不惟一;對於最後一種狀況顯然是?的數目影響的,乘法分佈原理 2cnt  , cnt表示?的數目

 

代碼

 45‘代碼   後邊超時辣

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>

using namespace std;


string s[100009],s1;
int n,m,q;
int qus,num,l,r;
long long ans=0;


int main()
{
    freopen("lin.in","r",stdin);
    freopen("lin.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    
    if(q==0) return 0;
    
    for(int i=1;i<=m;i++)
      cin>>s[i];
      
    for(int i=1;i<=q;i++)
    {
        ans=0;
        scanf("%d",&qus);
        if(qus==0)
        {
            scanf("%d%d",&l,&r);
            
            int flag=520,cnt=0;
            
            for(int j=0;j<n;j++)
            {
                if(flag==0) break;
                bool glf=0;
                char mp;
                mp=s[l][j];
                if(mp!='?') glf=0;
                else glf=1;
                for(int k=l+1;k<=r;k++)
                {
                    if(s[k][j]!='?')
                    {
                        if(mp=='?')
                        {
                            glf=1;
                            mp=s[k][j];
                            continue;
                        }
                        if(mp!='?'&&mp!=s[k][j]) 
                        {
                            flag=0;
                            continue;
                        }
                        
                    }
                    
                    else if(s[k][j]=='?')
                    {
                        if(glf==0) glf=1;
                    }
                    
                    
                }
                if(mp!='?') glf=0;
                cnt+=glf;
            }
            
            if(flag==0) {printf("0\n");} 
            else
             {
                 ans=pow(2,cnt);
                printf("%ld\n",ans);
             
             }
        }
        if(qus==1)
        {
            scanf("%d",&num);
            cin>>s1;
            s[num]=s1;
        }
    }

    return 0;
}

 

 我百分之200的錯誤都是由於腦殘,下面這個是整理了一下(貌似並不能夠多得分)

#include<bits/stdc++.h>

using namespace std;

int n,m,q;
string s[100010],s1;
int opt,l,r,pos;

inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int main()
{
    freopen("lin.in","r",stdin);
    freopen("lin.out","w",stdout);
    
    n=read(); m=read(); q=read();
    for(int i=1;i<=m;i++)
      cin>>s[i];

    for(int i=1;i<=q;i++)
    {
        opt=read();
        if(opt==1)
        {
            pos=read();
            cin>>s1;
            s[pos]=s1;        
        }
        else
        {
            l=read();r=read();
            
            long long ans;
            int flag=1,cnt=0;
            
            for(int j=0;j<n;j++)
            {
                if(flag==0) break;
                int flag0=0,flag1=0,flag2=0;
                for(int k=l;k<=r;k++)
                {
                    if(s[k][j]=='0') flag0++;
                    if(s[k][j]=='1') flag1++;
                    if(s[k][j]=='?') flag2++;
                }
                
                if(flag0!=0&&flag1!=0) { flag=0; continue; }    
                else if(flag2==(r-l+1)) cnt++;
                 
            }
            
            if(flag==0) ans=0;
            else ans=pow(2,cnt);     
            printf("%ld\n",ans);
            
        }
    }
    
    return 0;
}

 

固然也能夠用按位與維護

 

 

2.考慮正解:線段樹

 

代碼

忍不了這個指針了

#include <cstdio>

template <typename T>

inline void qr(T &x) {
  char ch = getchar(), lst = ' ';
  while ((ch > '9') || (ch < '0')) lst = ch, ch=getchar();
  while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
  if (lst == '-') x = -x;
}   //快讀 

const int maxn = 100010;

int n, m, q;
char s[maxn][35];   //每封信的內容 

#ifdef ONLINE_JUDGE
int Ans;
#endif

struct Tree {
  Tree *ls, *rs;
  int l, r, x, y;
  bool leg;      //是否合法 

  Tree() {
    ls = rs = NULL;
    l = r = x = y = 0;
    leg = true;   //先假定合法 
  }

  void pushup() {   //構造a1,a2,b1,b2 
    if (!(this->ls->leg && this->rs->leg)) {
      this->leg = false;
    } else {
      if ((this->ls->x & this->rs->x) & (this->ls->y ^ this->rs->y)) {
        this->leg = false;
      } else {
        this->leg = true;
        this->x = this->ls->x | this->rs->x;
        this->y = this->ls->y | this->rs->y;
      }
    }
  }
};
Tree *rot;   //線段樹的根 

void ReadStr(char *p);   //指針  快讀 
void Update(const int x);
void Query(const int l, const int r);
void update(Tree *const u, const int p);  //修改 
Tree query(Tree *u, const int l, const int r);   //查詢 
void build(Tree *const u, const int l, const int r);   //建樹 

int main() {
  freopen("lin.in", "r", stdin);
  freopen("lin.out", "w", stdout);
  qr(n); qr(m); qr(q);
  for (int i = 1; i <= m; ++i) {
    ReadStr(s[i] + 1);
  }
  build(rot = new Tree, 1, m);   //建樹 
  int opt, l, r;
  while (q--) {
    opt = 0; qr(opt);
    if (opt == 0) {   //查詢 
      l = r = 0; qr(l); qr(r);
      Query(l, r);
    } else {
      l = 0; qr(l);
      ReadStr(s[0] + 1);
      Update(l);   //修改第一個 
    }
  }
#ifdef ONLINE_JUDGE   //鬼知道幹啥的 
  printf("%d\n", Ans);
#endif
  return 0;
}

void ReadStr(char *p) {
  do *p = getchar(); while ((*p != '0') && (*p != '1') && (*p != '?'));
  do *(++p) = getchar(); while ((*p == '0') || (*p == '1') || (*p == '?'));
  *p = 0;
}

void build(Tree *const u, const int l, const int r) {
  if ((u->l = l) == (u->r = r)) {   //當前區間在要建的區間內 
    for (int i = 1; i <= n; ++i) {
      if (s[l][i] != '?') {
        u->x |= 1 << i;
        if (s[l][i] == '1') {
          u->y |= 1 << i;
        }
      }
    }
  } else {  //不在 
    int mid = (l + r) >> 1;
    build(u->ls = new Tree, l, mid);
    build(u->rs = new Tree, mid + 1, r);
    u->pushup();
  }
}

Tree query(Tree *u, const int l, const int r) {
  if ((u->l > r) || (u->r < l)) return Tree();  //當前區間徹底不在查詢區間 
  if ((u->l >= l) && (u->r <= r)) return *u;  //徹底在 
  Tree _ret;
  auto ll = query(u->ls, l, r), rr = query(u->rs, l, r);  //不然遞歸來求 
  _ret.ls = &ll; _ret.rs = &rr;
  _ret.pushup();
  return _ret;
}

void Query(const int l, const int r) {
  auto _ret = query(rot, l, r);
  if (!_ret.leg) {  //不合法輸出‘0’ 
#ifndef ONLINE_JUDGE
    puts("0");
#endif
  } else {
    int ans = 1;
    for (int i = 1; i <= n; ++i) if (!(_ret.x & (1 << i))) {
      ans <<= 1;
    }
#ifdef ONLINE_JUDGE
    Ans ^= ans;
#else
    printf("%d\n", ans);
#endif
  }
}

void update(Tree *u, const int p) {  //修改第p個 
  if (u->ls) {  //尋找p 
    if (u->ls->r >= p) {  //左子樹右端點比p大 
      update(u->ls, p);
    } else {
      update(u->rs, p);
    }
    u->pushup();  //把子節點的信息合併到父節點 
  } else {
    *u = Tree();
    u->l = u->r = p;
    for (int i = 1; i <= n; ++i) {
      if (s[0][i] != '?') {
        u->x |= 1 << i;
        if (s[0][i] == '1') {
          u->y |= 1 << i;
        }
      }
    }
  }
}

void Update(const int x) {
  update(rot, x);   //修改 
}

拒絕理解Zay的代碼

 

 

放一隻water lift的代碼(線段樹板子)

#include <bits/stdc++.h>
using namespace std;
template <class T>
inline void read(T &num)
{
    bool flag = 0;
    num = 0;
    char c = getchar();
    while ((c < '0' || c > '9') && c != '-')
        c = getchar();
    if (c == '-')
    {
        flag = 1;
        c = getchar();
    }
    num = c - '0';
    c = getchar();
    while (c >= '0' && c <= '9')
        num = (num << 3) + (num << 1) + c - '0', c = getchar();
    if (flag)
        num *= -1;
}
template <class T>
inline void output(T num)
{
    if (num < 0)
    {
        putchar('-');
        num = -num;
    }
    if (num >= 10)
        output(num / 10);
    putchar(num % 10 + '0');
}
template <class T>
inline void outln(T num)
{
    output(num);
    putchar('\n');
}
template <class T>
inline void outps(T num)
{
    output(num);
    putchar(' ');
}
const int N = 31, M = 100010;
int n, m, q;
struct segment
{
    char val[M];
    bool all1[M * 4];
    bool all0[M * 4];
    void init(int node, int nl, int nr)//這是個正常的線段樹的板子(雖然位置好像不大正常)
    {
        if (nl < nr)
        {
            int mid = (nl + nr) >> 1;
            init(node * 2, nl, mid);
            init(node * 2 + 1, mid + 1, nr);
            all1[node] = all1[node * 2] & all1[node * 2 + 1];
            all0[node] = all0[node * 2] & all0[node * 2 + 1];
        }
        else
        {
            if (val[nl] == '?')
                all1[node] = all0[node] = 1;
            else
            {
                all1[node] = val[nl] == '1';
                all0[node] = val[nl] == '0';
            }
        }
    }
    void modify(int node, int nl, int nr, int x, char va)
    {
        if (val[x] == va)
            return;
        if (nl < nr)
        {
            int mid = (nl + nr) >> 1;
            if (x <= mid)
            {
                modify(node * 2, nl, mid, x, va);
            }
            else
            {
                modify(node * 2 + 1, mid + 1, nr, x, va);
            }
            all1[node] = all1[node * 2] & all1[node * 2 + 1];
            all0[node] = all0[node * 2] & all0[node * 2 + 1];
        }
        else
        {
            if (va == '?')
                all1[node] = all0[node] = 1;
            else
            {
                all1[node] = va == '1';
                all0[node] = va == '0';
            }
            val[x] = va;
        }
    }
    pair<bool, bool> query(int node, int nl, int nr, int l, int r)
    {
        if (l <= nl && r >= nr)
        {
            return make_pair(all1[node], all0[node]);
        }
        int mid = (nl + nr) >> 1;
        bool a1 = true, a0 = true;
        if (l <= mid)
        {
            auto lo = query(node * 2, nl, mid, l, r);
            a1 &= lo.first;
            a0 &= lo.second;
        }
        if (r >= mid + 1)
        {
            auto lo = query(node * 2 + 1, mid + 1, nr, l, r);
            a1 &= lo.first;
            a0 &= lo.second;
        }
        return make_pair(a1, a0);
    }
    void dfs(int node, int nl, int nr)
    {
        if (nl < nr)
        {
            int mid = (nl + nr) >> 1;
            dfs(node * 2, nl, mid);
            dfs(node * 2 + 1, mid + 1, nr);
        }
        outps(nl);
        outps(nr);
        outps(all1[node]);
        outln(all0[node]);
    }
} segs[N];
int main()
{
    freopen("lin.in", "r", stdin);
    freopen("lin.out", "w", stdout);
    read(n);
    read(m);
    read(q);
    char ch;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            do
            {
                ch = getchar();
            } while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\0');
            segs[j].val[i] = ch;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        segs[i].init(1, 1, m);
    }
    while (q--)
    {
        bool opt;
        read(opt);
        if (opt == 0)
        {
            int l, r;
            read(l);
            read(r);
            int ans = 1;
            for (int i = 1; i <= n; i++)
            {
                auto lo = segs[i].query(1, 1, m, l, r);
                ans *= (lo.first + lo.second);
            }
            outln(ans);
        }
        else
        {
            int pos;
            read(pos);
            for (int i = 1; i <= n; i++)
            {
                do
                {
                    ch = getchar();
                } while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\0');
                segs[i].modify(1, 1, m, pos, ch);
            }
        }
    }
}

 

 

 


 

彩蛋:

考試暴露了不少小問題

1.文件的讀寫

安利一個方法:如何比較本身的代碼ans和答案out是否一致(不只是用在NOIP上,還有NOI,IOI)

(1)鍵盤按住 shift 鍵,同時鼠標右鍵單擊

(2)會崩出一個會話框,點擊「在此處打開命令窗口」,win10不大同樣,好像是f**忘了

(3)就會出現這個東西

不輸入cmd 也能夠,由於它自己就是cmd

 

(4)輸入  fc  文件1  文件2

若是不一樣的話

若是相同的話

 

這樣你就能夠看看本身的答案對不對,錯在哪裏QWQ

 

 

2.數據分治

也就是對於不一樣的子任務你能夠用不一樣的方法解決掉,從而騙取更多分數

 

 

3.編譯&加文件的順序

  大型考試的時候呢,若是你代碼一旦進行了修改,那麼必定要從新試一遍樣例

  freopen就不要註釋掉啦

 

4.題目難度和順序安排不必定有瓜

相關文章
相關標籤/搜索