2019.11.11 題解報告


2019.11.11 題解報告

\[N^2\text{狂草1e5它不香嘛?}\]
\[\text{By:Unluckierblock}\]ios

答題狀況:

  • 總成績 : 169, 排名: 11 / 32
  • T1 : 0 T2 : 99 T3 : 70

各題目分析:

  • 題目 1 :
    預估成績 : 60 實際成績 : 0 考試用時 : 8 : 00 ~ 8 : 50 , 9 : 50 ~ 10 : 10c++

    沒有什麼感受 , 就先寫了 暴力30
    寫完T3以後再來看 T1 , 優化到了60分
    測了 大樣例的前10組數據, 都沒有問題 , 因而就無論了git

    然而樣例比較獨特 , 沒有卡掉錯誤作法 , 最後仍是爆了零大數據

    • 教訓 :
      測大樣例時必定要測完 , 若是超時比較嚴重 就和暴力進行對拍
  • 題目 2 :
    預估成績 : 99 實際成績 : 99 考試用時 : 8 : 50 ~ 9 : 10 , 10 : 15 ~ 10 : 54優化

    先寫了一發暴力 .
    以爲暴力過不去大數據 , 就重構代碼 , 寫了一發線段樹
    手比較順 , 很快調了出來 , 沒有問題 , 過了大樣例ui

  • 題目 3 :
    預估成績 : 40 實際成績 : 70 考試用時 : 9 : 10 ~ 9 : 47spa

    發現了比較優美的性質 , 寫了搜索
    很快寫完以後 過了大樣例code


題目解析:

T1:遞歸

60 % 數據 :
預處理 1 ~ 10000內 全部數的約數
對於每個N, \(O(n ^ 2)\) 枚舉其兩個約數 L, W
並計算出H = N - L - W.
若H是否也爲N的約數 , 則枚舉的L, W, H爲合法的三元組 , 對此類合法三元組統計答案便可

100 %數據 :
對計算式進行轉化 :
L + W + H = N
因爲 L, W, H都爲 N的約數, 則有:
\(\frac{1}{k1} * N + \frac{1}{k2} * N + \frac{1}{k3} * N = N\)
則有 : \((\frac{1}{k1} + \frac{1}{k2} +\frac{1}{k3}) = 1\)
能夠發現, 對於k1, k2, k3的取值, 只有下列三種狀況:

  1. \(2\ 3\ 6\) 需知足N是6的倍數 , 貢獻爲 : \(\frac{N}{2} * \frac{N}{3} *\frac{N}{6} = \frac{N^3}{36}\)
  2. \(3\ 3\ 3\) 需知足N是3的倍數 , 貢獻爲 : \(\frac{N}{3} * \frac{N}{3} * \frac{N}{3} = \frac{N ^ 3}{27}\)
  3. \(2\ 4\ 4\) 需知足N是4的倍數 , 貢獻爲 : \(\frac{N}{2} * \frac{N}{4} * \frac{N}{4} = \frac{N ^ 3}{32}\)

能夠發現, 對於一個 N , 知足狀況1, 必然知足狀況2
且狀況2的貢獻更大 顯然狀況2更優

則只需考慮 N是否爲 3或4 的倍數便可
若N爲3的倍數 ,因爲狀況2的貢獻較大 , 則答案即爲 \(\frac{N}{3} * \frac{N}{3} * \frac{N}{3} = \frac{N ^ 3}{27}\)
不然, 若N爲4的倍數, 則答案即爲 \(\frac{N}{2} * \frac{N}{4} * \frac{N}{4} = \frac{N ^ 3}{32}\)
不然無解


T2:

不穩定作法:

  1. 直接暴力模擬:
    每一次都將整個序列掃一遍, 對可以戰勝的怪物幹掉, 並增長屬性值
    當有一回合沒法幹掉任何怪物時, 中止循環
    總複雜度O((n ^ 2) * K) 數據水因此卡了過去

  2. 線段樹
    使用線段樹,
    維護區間內怪物 各屬性值的最大/最小值
    維護區間內怪物 戰勝後增長的各屬性的和

  3. 若當前勇者全部屬性值 > 區間內怪物全部屬性最大值,
    說明此區間內的怪物 當前所有能夠被戰勝, 統計此區間的總貢獻
    更新當前區間的值, 並回溯
  4. 若當前勇者某一屬性值 < 區間內怪物屬性某一最小值,
    則此區間內的怪物 當前都不能被戰勝 , 回溯便可
  5. 若不知足上述2, 3條件, 繼續向下遞歸

顯然, 此作法單次查詢 最多會被卡到nlogn
可是數據水因此卡了過去

正解:
創建 K 個小根堆,先把全部怪物按第一屬性爲關鍵字放入第一個堆.

每輪操做均從第一個堆開始, 檢查堆頂元素對應第一屬性值 是否比勇士對應屬性值小.
如果, 則彈出放入第二個以第二屬性值爲關鍵字的堆.
一樣的檢查第二個堆的堆頂元素對應的第二屬性值是否比勇士對應屬性值小,
如果則彈出放入第三個以第三屬性值爲關鍵字的小根堆.....

直到第 K 個堆,若彈出第 K 個堆, 說明該怪物的 K 個屬性值都小於等於勇士的屬性值,
經過戰勝該怪物來更新勇士的屬性值.

每一個怪物最多入堆出堆 K次,因此時間複雜度爲 O(KNlogN).


T3:

題目要求:
給定一結點數爲N 的圖, 初始圖中沒有邊
給定M此操做, 每次操做會使圖中 增長/刪除 一條邊
求每次修改後, 剛好匹配1, 2, ... N/2 對點的方案數

70% 數據:
題目能夠轉化爲:
求選擇k 條 沒有重複端點的邊的方案數 \((1 <= k <= N/2)\)
則可搜索枚舉 每一條邊的選擇狀況
每枚舉一條邊, 對其兩端點打標記, 保證不出現 選擇重複選擇端點狀況
當全部邊枚舉完後, 則找到了一個合法匹配 , 爲對應答案相加便可

100% 數據:
狀壓 DP. 用 \(f[i][S]\) 表示前 i 個操做都考慮上之後 , 已匹配的頂點狀態爲S 的方案數.
因而, 加入一條新的邊後, 全部方案能夠分爲兩類, 第一類是包含新加的邊, 第二類是不包含新加的邊.
兩類方案的方案數分別爲 \(f[i-1][S-(1<<u)-(1<<v)]\)\(f[i-1][S]\) ,
\(f[i][S] = f[i-1][S] + f[i-1][S-(1<<u)-(1<<v)]\)
至於刪邊,就是把加邊逆過來,
\(f[i][S] = f[i-1][S] - f[i-1][S-(1<<u)-(1<<v)]\)
時間複雜度爲 O(M*2^(N/2)).


代碼實現:

T1:

  • 考場代碼 :
#include <cstdio>
#include <vector>
#include <ctype.h>
#define max(a, b) (a > b ? a : b)
#define ll long long
const int MARX = 1e6 + 10;
//=============================================================
int T;
ll N, ans[MARX];
std :: vector <int> s[MARX];
//=============================================================
inline int read()
{
    int s = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
    return s * w;
}
//=============================================================
int main()
{
    freopen("diy.in", "r", stdin);
    freopen("diy.out", "w", stdout);
    for(int i = 1; i <= 100000; i ++)
      for(int j = 1; j * j <= i; j ++)
        if(i % j == 0) s[i].push_back(j);
    
    T = read();
    while(T --)
    {
       N = (ll) read();
       if(ans[N]) {printf("%lld\n", ans[N]); continue;}
       else ans[N] = - 1;
       
       if(N <= 10000)
       {
         for(ll i = 0, size = s[N].size(); i < size; i ++)
           for(ll j = i; j < size; j ++)
             if(s[N][i] + s[N][j] < N && N % (N - s[N][i] - s[N][j]) == 0)
               ans[N] = max(ans[N], s[N][i] * s[N][j] * (N - s[N][i] - s[N][j])); 
       }
       else
       {
         for(ll i = 1; i <= N - 2; i ++)
           if(N % i == 0)
             for(ll j = i; j <= N - 2; j ++)
               if(i + j < N && N % j == 0 && N % (N - i - j) == 0)
                 ans[N] = max(ans[N], i * j * (N - i - j));
       }
       printf("%lld\n", ans[N]);
    }
    return 0;
}
  • 正解 :
#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    freopen("diy.in", "r", stdin);
    freopen("diy.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while(T--) {
        int n;
        cin >> n;
        if(n % 3 == 0) {
            int t = n / 3;
            cout << t * 1LL * t * t << '\n';
        } else if(n % 4 == 0) {
            int t1 = n / 2;
            int t2 = n / 4;
            cout << t1 * 1LL * t2 * t2 << '\n';
        } else {
            cout << "-1\n";
        }
    }
    return 0;
}

T2:

  • 考場代碼:
#include <cstdio>
#include <ctype.h>
#define ls (now << 1)
#define rs (now << 1 | 1)
#define max(a, b) (a > b ? a : b)
#define min(a, b) (a < b ? a : b)
#define ll long long
const int MARXK = 10;
const int MARX = 2e5 + 10;
//=============================================================
struct node
{
    int L, R, use, sum;
    int maxa[MARXK], mina[MARXK], sumb[MARXK];
}tree[MARX << 2];
struct Monster
{
    int a[MARXK], b[MARXK];
}mon[MARX];
int ans, T, N, K, v[MARXK];
//=============================================================
inline int read()
{
    int s = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
    return s * w;
}
void pushup(int now)
{
    for(int i = 1; i <= K; i ++)
      tree[now].maxa[i] = max(tree[ls].maxa[i], tree[rs].maxa[i]), 
      tree[now].mina[i] = min(tree[ls].mina[i], tree[rs].mina[i]),
      tree[now].sumb[i] = tree[ls].sumb[i] + tree[rs].sumb[i],
      tree[now].sum = tree[ls].sum + tree[rs].sum;
}
void Build(int now, int L, int R)
{
    tree[now].L = L, tree[now].R = R;
    if(L == R)
    {
      for(int i = 1; i <= K; i ++) 
        tree[now].maxa[i] = mon[L].a[i], 
        tree[now].mina[i] = mon[L].a[i], 
        tree[now].sumb[i] = mon[L].b[i];
      tree[now].sum = 1;
      return ;
    }
    int mid = (L + R) >> 1;
    Build(ls, L, mid), Build(rs, mid + 1, R);
    pushup(now);
}
int search(int now)
{
    bool flagdown = 1, flagup = 1;
    for(int i = 1; i <= K; i ++)
    {
      if(tree[now].maxa[i] > v[i]) flagup = 0;
      if(tree[now].mina[i] > v[i]) flagdown = 0;
    }
    if(flagup)
    {
      for(int i = 1; i <= K; i ++) 
        v[i] += tree[now].sumb[i],
        tree[now].sumb[i] = 0,
        tree[now].maxa[i] = - 1, 
        tree[now].mina[i] = MARX;
      int sum = tree[now].sum; tree[now].sum = 0;
      return sum;
    }
    if(! flagdown) return 0;
    
    int ret = 0;
    if(tree[ls].sum) ret += search(ls);
    if(tree[rs].sum) ret += search(rs);
    pushup(now);
    return ret;
}
//=============================================================
signed main()
{
    freopen("tower.in", "r", stdin);
    freopen("tower.out", "w", stdout);
    T = read();
    while(T --)
    {
      N = read(), K = read(); ans = 0;
      for(int i = 1; i <= K; i ++) v[i] = read();
      for(int i = 1; i <= N; i ++)
      {
        for(int j = 1; j <= K; j ++) mon[i].a[j] = read();
        for(int j = 1; j <= K; j ++) mon[i].b[j] = read();
      }
      
      Build(1, 1, N);
      while(1)
      {
        int plus = search(1);
        if(! plus) break;
        ans += plus;
      }
      
      printf("%d\n", ans);
      printf("%d", v[1]);
      for(int i = 2; i <= K; i ++) printf(" %d", v[i]);
      printf("\n");
    }
}
//暴力: 
/*
#include <cstdio>
#include <ctype.h>
#include <queue>
#include <algorithm>
#include <cstring>
#define ll long long
const int MARXK = 7;
const int MARX = 1e5 + 10;
//=============================================================
struct Monster
{
    int a[MARXK], b[MARXK];
}mon[MARX];
int ans, T, N, K, v[MARXK];
bool vis[MARX];
//=============================================================
inline int read()
{
    int s = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
    return s * w;
}
void solve1()
{
    std :: priority_queue <std :: pair <int, int> > q;
    for(int i = 1; i <= K; i ++) v[i] = read();
    for(int i = 1; i <= N; i ++)
    {
      int fir = read(), sec = read();
      q.push(std :: make_pair(- fir, sec));
    }
    for(; - 1 *  q.top().first <= v[1]; )
    {
      ans ++, v[1] += q.top().second;
      q.pop();
      if(q.empty()) break;
    }
    printf("%d\n%d\n", ans, v[1]);
}
int search()
{
    for(int i = 1; i <= N; i ++)
      if(! vis[i])
      {
        bool flag = 1;
        for(int j = 1; j <= K; j ++)
          if(mon[i].a[j] > v[j]) {flag = 0; break;}
        if(flag) {vis[i] = 1; return i;}
      }
    return - 1;
}
//=============================================================
int main()
{
    T = read();
    while(T --)
    {
      memset(vis, 0, sizeof(vis)); ans = 0;
        
      N = read(), K = read();
      if(K == 1) {solve1(); continue;}
      for(int i = 1; i <= K; i ++) v[i] = read();
      for(int i = 1; i <= N; i ++)
      {
        for(int j = 1; j <= K; j ++) mon[i].a[j] = read();
        for(int j = 1; j <= K; j ++) mon[i].b[j] = read();
      }
      
      for(int i = 1; i <= N; i ++)
      {
        int pos = search();
        if(pos == -1) break;
        
        ans ++;
        for(int j = 1; j <= K; j ++) v[j] += mon[pos].b[j];
      }
      
      printf("%d\n", ans);
      for(int i = 1; i <= K; i ++) printf("%d ", v[i]);
      printf("\n");
    }
    return 0;
}
/*
1 4 1
1
1 1
4 2
2 3
10 1
*/
  • 正解 :
#include<bits/stdc++.h>
#define N 100010
#define INF 0x3f3f3f3f
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lowwer_bound
#define eps 1e-8
const LL mod=1e9+7;
using namespace std;
typedef pair<int,int> P;
priority_queue<P,vector<P>,greater<P> >q[5];

int a[N][5],b[N][5],v[5];
namespace IO{
    #define BUF_SIZE 100000
    #define OUT_SIZE 100000
    #define ll long long
    //fread->read
    bool IOerror=0;
    inline char nc(){
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if (p1==pend){
            p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if (pend==p1){IOerror=1;return -1;}
            //{printf("IO error!\n");system("pause");for (;;);exit(0);}
        }
        return *p1++;
    }
    inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
    inline void read(int &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (sign)x=-x;
    }
};
 
int main()
{
    freopen("tower.in", "r", stdin);
    freopen("tower.out", "w", stdout);

    int T;
    IO::read(T);
    while(T--)
    {
        int n,m;
        IO::read(n);IO::read(m);
        for(int i=0;i<m;i++) while(!q[i].empty()) q[i].pop();
        for (int i=0;i<m;i++) IO::read(v[i]);
        for (int i=1;i<=n;i++)
        {
            for (int j=0;j<m;j++) IO::read(a[i][j]);
            for (int j=0;j<m;j++) IO::read(b[i][j]);
            q[0].push(P(a[i][0],i));
        }
        int p=0,ans=0;
        while(1)
        {
            for (int i=0;i<m-1;i++)
            {
                while(!q[i].empty() && q[i].top().first<=v[i])
                {
                    int x=q[i].top().second; q[i].pop();
                    q[i+1].push(P(a[x][i+1],x));
                }
            }
            while (!q[m-1].empty() && q[m-1].top().first<=v[m-1])
            {
                ans++;
                int x=q[m-1].top().second; q[m-1].pop();
                for (int i=0;i<m;i++) v[i]+=b[x][i];
            }
            if (p==ans) break;
            p=ans;
        }
        cout<<ans<<endl;cout<<v[0];
        for (int i=1;i<m;i++) cout<<' '<<v[i];
        cout<<endl;
    }
    return 0;
}

T3:

  • 考場代碼:
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const ll mod = 1e9 + 7;
const int MARX = 15;
//=============================================================
int T, N, M, cnt[MARX], color[MARX];
ll ans[MARX];
bool map[MARX][MARX], use[MARX];
//=============================================================
inline int read()
{
    int s = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
    return s * w;
}
void dfs(int now, int sum)
{
    if(now > N) {ans[sum] = (ans[sum] + 1ll) % mod; return;}
    dfs(now + 1, sum);
    
    if(use[now]) return ;
    for(int i = now + 1; i <= N; i ++)
      if(map[now][i] && use[i] == 0)
      {
        map[now][i] = map[i][now] = 0, use[now] = use[i] = 1;
        dfs(now + 1, sum + 1);
        map[now][i] = map[i][now] = 1, use[now] = use[i] = 0;
      }
}
//=============================================================
signed main()
{
    freopen("sport.in", "r", stdin);
    freopen("sport.out", "w", stdout);
    T = read();
    while(T --)
    {
      memset(map, 0, sizeof(map));
      
      N = read(), M = read();
      for(int i = 1; i <= M; i ++)
      {
        memset(ans, 0, sizeof(ans));
        memset(use, 0, sizeof(use));
              
        char opt; int u, v;
        scanf("%c", &opt); u = read(), v = read();
        map[u][v] = map[v][u] = (opt == '+');
        dfs(1, 0);
        
        printf("%lld", ans[1]);
        for(int i = 2; i <= N / 2; i ++) printf(" %lld", ans[i]);
        printf("\n");
      }
    }
}
  • 正解 :
/*dp[i][j]表示j狀態點集的方案數*/
# include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
int dp[2][1025], num[1025], sta[1025], ans[6];
void add(int &x, int y)
{
    x += y;
    if(x >= mod) x -= mod;
}
void sub(int &x, int y)
{
    x -= y;
    if(x < 0) x += mod;
}
int main()
{
    freopen("sport.in", "r", stdin);
    freopen("sport.out", "w", stdout);
    char c;
    int n, q, T, u, v, cnt=0;
    for(int i=0; i<1024; ++i)
    {
        num[i]= num[i>>1]+(i&1);//i的二進制有幾個1
        if(~num[i]&1) sta[cnt++] = i;//sta保存偶數個1的二進制數
    }
    for(scanf("%d",&T);T;--T)
    {
        memset(dp, 0, sizeof(dp));
        scanf("%d%d",&n,&q);
        int now = 0;
        dp[0][0] = 1;
        while(q--)
        {
            getchar();
            c = getchar();
            scanf("%d%d",&u,&v);
            --u,--v;
            memset(ans, 0, sizeof(ans));
            int tmp = (1<<u) | (1<<v);
            if(c == '+')
            {
                for(int i=0; i<cnt&&sta[i]<(1<<n); ++i)
                {
                    int cur = sta[i];
                    dp[now^1][cur] = dp[now][cur];
                    if((cur&tmp) == tmp) add(dp[now^1][cur],dp[now][cur^tmp]);
                    add(ans[num[cur]/2],dp[now^1][cur]);
                }
            }
            else
            {
                for(int i=0; i<cnt&&sta[i]<(1<<n); ++i)
                {
                    int cur = sta[i];
                    dp[now^1][cur] = dp[now][cur];
                    if((cur&tmp) == tmp) sub(dp[now^1][cur],dp[now][cur^tmp]);
                    add(ans[num[cur]/2],dp[now^1][cur]);
                }
            }
            now ^= 1;
            for(int i=1; i<=n/2; ++i) printf("%d%c",ans[i],i==n/2?'\n':' ');
        }
    }
    return 0;
}
相關文章
相關標籤/搜索