2019.11.11 题解报告

Posted luckyblock

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019.11.11 题解报告相关的知识,希望对你有一定的参考价值。


2019.11.11 题解报告

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

答题情况:

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

各题目分析:

  • 题目 1 :
    预估成绩 : 60 实际成绩 : 0 考试用时 : 8 : 00 ~ 8 : 50 , 9 : 50 ~ 10 : 10

    没有什么感觉 , 就先写了 暴力30
    写完T3之后再来看 T1 , 优化到了60分
    测了 大样例的前10组数据, 都没有问题 , 于是就不管了

    然而样例比较独特 , 没有卡掉错误做法 , 最后还是爆了零

    • 教训 :
      测大样例时一定要测完 , 如果超时比较严重 就和暴力进行对拍
  • 题目 2 :
    预估成绩 : 99 实际成绩 : 99 考试用时 : 8 : 50 ~ 9 : 10 , 10 : 15 ~ 10 : 54

    先写了一发暴力 .
    觉得暴力过不去大数据 , 就重构代码 , 写了一发线段树
    手比较顺 , 很快调了出来 , 没有问题 , 过了大样例

  • 题目 3 :
    预估成绩 : 40 实际成绩 : 70 考试用时 : 9 : 10 ~ 9 : 47

    发现了比较优美的性质 , 写了搜索
    很快写完之后 过了大样例


题目解析:

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
", 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
", 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 << '
';
        } else if(n % 4 == 0) {
            int t1 = n / 2;
            int t2 = n / 4;
            cout << t1 * 1LL * t2 * t2 << '
';
        } else {
            cout << "-1
";
        }
    }
    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
", ans);
      printf("%d", v[1]);
      for(int i = 2; i <= K; i ++) printf(" %d", v[i]);
      printf("
");
    }
}
//暴力: 
/*
#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
%d
", 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
", ans);
      for(int i = 1; i <= K; i ++) printf("%d ", v[i]);
      printf("
");
    }
    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!
");system("pause");for (;;);exit(0);}
        }
        return *p1++;
    }
    inline bool blank(char ch){return ch==' '||ch=='
'||ch=='
'||ch=='	';}
    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("
");
      }
    }
}
  • 正解 :
/*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?'
':' ');
        }
    }
    return 0;
}

以上是关于2019.11.11 题解报告的主要内容,如果未能解决你的问题,请参考以下文章

题解报告(CDUT暑期集训——第三场)

题解报告:hdu 4704 Sum

题解报告:hdu 1406 完数

题解报告:hdu 1228 A+B

题解报告:hdu1994利息计算

题解报告——Financial Crisis