2019 CCPC-Wannafly Winter Camp Day1 (Div2, onsite)

solve:4/11

补题:6/11

A 机器人 

补题:zz

这是一道分类讨论的题目,有一个规律就是如果必须要从第一个区到第二个区,那么最多转区两次(1到2一次,2到1一次),然后分类讨论即可,只要细心一定能做出来。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<math.h>
#include<cmath>
#include<time.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<numeric>
#include<stack>
#include<bitset>
#include<unordered_map>
const int maxn = 0x3f3f3f3f;
const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427;
const double PI = 3.141592653589793238462643383279;
//#ifdef TRUETRUE
//#define gets gets_s
//#endif
using namespace std;
int c[2][1000010];
int z[2][100010], cnt[5];
int d[111];
int main(void)
{
    //ios::sync_with_stdio(false);
    int n, rr, k, m, s, i, x, y, l1, l2, r1, r2, ans, l, r, j;
    while (~scanf("%d %d %d %d %d", &n, &rr, &m, &k, &s))
    {
        memset(c, 0, sizeof(c));
        cnt[0] = 0;
        cnt[1] = 0;
        for (i = 0; i < rr; i++)
        {
            scanf("%d %d", &x, &y);
            if (x == s && y == 0)
            {
                continue;
            }
            c[y][x] = 1;
            z[y][cnt[y]++] = x;
        }
        for (i = 0; i < m; i++)
        {
            scanf("%d", d + i);
        }
        sort(z[0], z[0] + cnt[0]);
        sort(z[1], z[1] + cnt[1]);
        l1 = -1;
        l2 = -1;
        r1 = -1;
        r2 = -1;
        if (cnt[0])
        {
            l1 = z[0][0];
            r1 = z[0][cnt[0] - 1];
        }
        if (cnt[1])
        {
            l2 = z[1][0];
            r2 = z[1][cnt[1] - 1];
        }
        ans = 0;
        if (l2 != -1)
        {
            ans += k * 2;
        }
        l = min(l1, l2);
        if (l1 == -1)
        {
            l = l2;
        }
        else if (l2 == -1)
        {
            l = l1;
        }
        r = max(r1, r2);
        d[m++] = 1;
        d[m++] = n;
        d[m++] = n + 1;
        sort(d, d + m);
        /*for (i = 0;i < m;i++)
        {
        printf("%d ", d[i]);
        }
        printf("
");*/
        //printf("l = %d  r = %d
", l, r);
        if (r <= s && r != -1 && l != -1)
        {
            //printf("l = %d  r = %d
",l,r);
            for (i = 0; i < m; i++)
            {
                if (d[i] <= l && d[i + 1] > l)
                {
                    break;
                }
            }
            ans += s - d[i];
            //printf("ans = %d
",ans);
            if (l2 != -1)
            {
                for (j = 0; j < m; j++)
                {
                    if (d[j] >= r2)
                    {
                        break;
                    }
                }
                ans += d[j] - d[i];
                ans += abs(s - d[j]);
            }
            else
            {
                ans += s - d[i];
            }
        }
        else if (l >= s && r != -1 && l != -1)
        {
            for (i = 0; i < m; i++)
            {
                if (d[i] >= r)
                {
                    break;
                }
            }
            ans += d[i] - s;
            if (l2 != -1)
            {
                for (j = m - 1; j >= 0; j--)
                {
                    if (d[j] <= l2)
                    {
                        break;
                    }
                }
                ans += d[i] - d[j];
                ans += abs(s - d[j]);
            }
            else
            {
                ans += d[i] - s;
            }
        }
        else if (l <= s && r >= s && r != -1 && l != -1)
        {
            for (i = 0; i < m; i++)
            {
                if (d[i] <= l && d[i + 1] > l)
                {
                    break;
                }
            }
            //printf("   %d
",ans);
            ans += 2 * (s - d[i]);
            //printf("   %d %d %d %d %d
", ans,d[i],i,l,r);
            for (i = 0; i < m; i++)
            {
                if (d[i] <= r && d[i + 1] >= r)
                {
                    break;
                }
            }
            ans += 2 * (d[i + 1] - s);
            //printf("   %d %d
", ans,d[i + 1]);
        }
        printf("%d
", ans);
    }
    return 0;
}
View Code

B 吃豆豆

Code:pai爷

Thinking: pai爷  KK

      dp[ i ][ j ][ k ]表示 i 行 j 列 在第 k 秒的时候吃到的最多的豆豆数量,从五个方向转移(上下左右加自己)。k的范围最大只有1018*10,然后用滚动数组滚动掉,就可以AC了。

补充:div1 1018变成了10^18,会有周期,尚未证明,k,k+t,k+2t。

#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int ans=0,n,m,c,now=0,X1,X2,Y1,Y2,f[3][20][20],t[20][20];
void solve(int a,int b,int c,int d,int k) {
    if(t[a][b]!=0 && (k%t[a][b]==0)) f[!now][a][b]=max(f[now][c][d]+1,f[!now][a][b]);
    else f[!now][a][b]=max(f[now][c][d],f[!now][a][b]);
}
int main() {
    scanf("%d%d%d",&n,&m,&c);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++) scanf("%d",&t[i][j]);
    scanf("%d%d%d%d",&X1,&Y1,&X2,&Y2);
    for(int k=0; k<=1; k++)
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++) f[k][i][j]=-1;
    f[now][X1][Y1]=0;
    for(int k=1; k<=11000; k++) {
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                if(f[now][i][j]!=-1) {
                    solve(i+1,j,i,j,k);
                    solve(i-1,j,i,j,k);
                    solve(i,j+1,i,j,k);
                    solve(i,j-1,i,j,k);
                    solve(i,j,i,j,k);
                    if(f[!now][X2][Y2]>=c) ans=k;
                }
        if(ans) break;
        now=!now;
    }
    printf("%d
",ans);
}
View Code

C  拆拆拆数

unsolved

D 超难的数学题

unsolved

E 流流流动

补题:kk

树形dp,比赛没人做,以为是难题。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define CLR(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=10010;
int head[maxn],tot,n;
bool vis[maxn];
int f[maxn],d[maxn];
int dp[2][maxn];
struct edge{
    int v,Next;
}a[maxn<<2];
void init(){
    tot=0,CLR(head,-1);
    CLR(vis,false);
    CLR(dp,0);
}
void addv(int u,int v)
{
    a[++tot].v=v;
    a[tot].Next=head[u];
    head[u]=tot;
}
void tdfs(int u,int fa){
    int flag=0;
    vis[u]=1;
    dp[0][u]=0;
    dp[1][u]=f[u];
    for(int i=head[u];i!=-1;i=a[i].Next)
    {
        int v=a[i].v;
        if(v==fa)continue;
        tdfs(v,u);
        flag=1;
        dp[0][u]+=max(dp[0][v],dp[1][v]);
        dp[1][u]+=max(dp[0][v],dp[1][v]-d[min(u,v)]);
    }
}
int main(){
    cin>>n;
    init();
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&d[i]);
    }    
    for(int i=2;i<=n;i++)
    {
        
        if(i%2==1)
        {
            addv(i,3*i+1);
            addv(3*i+1,i);
        }
        if(i%2==0){
            addv(i,i/2);
            addv(i/2,i);
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==0){
            tdfs(i,0);
            ans+=max(dp[0][i],dp[1][i]);
        }
    }
    printf("%d
",ans);
}
View Code

F 爬山

Code:KK

Thinking:KK

      对于一座山,如果要砍,就肯定要砍到(K+第一座山的高度),将这部分额外的花费加到边的权值上就好了,双向边要分开建,权值不一定一样,然后dijkstra跑一跑,水题。

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 #include<string.h>
 5 #include<string>
 6 #include<math.h>
 7 #include<cmath>
 8 #include<time.h>
 9 #include<map>
10 #include<set>
11 #include<vector>
12 #include<queue>
13 #include<algorithm>
14 #include<numeric>
15 #include<stack>
16 #include<bitset>
17 //#include<unordered_map>
18 const int inf = 0x3f3f3f3f;
19 const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427;
20 const double PI = 3.141592653589793238462643383279;
21 const int maxn=100010;
22 using namespace std;
23 typedef long long ll;
24 int n,m;
25 ll k;
26 int head[maxn],tot;
27 ll dis[maxn];
28 
29 struct edge{
30     int v;
31     ll w;
32     int Next;
33 }a[maxn<<2];
34 bool vis[maxn];
35 ll h[maxn];
36 void init(){
37     tot=0,memset(head,-1,sizeof(head));
38 }
39 void addv(int u,int v,ll w)
40 {
41     a[++tot].v=v;
42     a[tot].w=w;
43     a[tot].Next=head[u];
44     head[u]=tot;
45 }
46 priority_queue< pair<ll,int>>q;
47 void dijkstra(){
48     memset(vis,false,sizeof(vis));
49     memset(dis,0x3f,sizeof(dis));
50     dis[1]=0;
51     q.push(make_pair(0,1));
52     while(!q.empty()){
53         int x=q.top().second;
54         q.pop();
55         if(vis[x])continue;
56         vis[x]=1;
57         for(int i=head[x];i!=-1;i=a[i].Next)
58         {
59             int y=a[i].v;
60             ll w=a[i].w;
61             if(dis[y]>dis[x]+w){
62                 dis[y]=dis[x]+w;
63                 q.push(make_pair(-dis[y],y));
64             }
65         }
66     }
67 }
68 int main(void)
69 {
70     //ios::sync_with_stdio(false);
71     cin>>n>>m>>k;
72     init();
73     scanf("%lld",&h[1]);
74     k+=h[1];
75     for(int i=2;i<=n;i++)
76     {
77         scanf("%lld",&h[i]);
78     }
79     int u,v;
80     ll w;
81     while(m--)
82     {
83         scanf("%d%d%lld",&u,&v,&w);
84         if(h[v]>=k)
85         addv(u,v,w+(h[v]-k)*(h[v]-k));
86         else addv(u,v,w);
87         if(h[u]>=k)
88         addv(v,u,w+(h[u]-k)*(h[u]-k));
89         else addv(v,u,w);
90     }
91     dijkstra();
92     printf("%lld
",dis[n]);
93     return 0;
94 }
View Code

G  双重矩阵

unsolved

H 我爱割葱

unsolved

I 起起落落

Code:pai爷

Thinking:pai爷

div 2 树状数组+dp思想 O(n*n*log(n))

 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 using namespace std;
 8 const int P=1e9+7;
 9 int n,p[2010],w[2010],c[10100],q[2010][2010];
10 int ans=0;
11 inline int lowbit(int x){return x&-x;} 
12 void add(int x,int y)
13 {
14     for(;x<=n;x+=lowbit(x)) c[x]=(c[x]+y)%P;
15 }
16 int sum(int x)
17 {
18     int tmp=0;
19     for(;x;x-=lowbit(x)) tmp=(tmp+c[x])%P;
20     return tmp;
21 }
22 int main()
23 {
24     scanf("%d",&n);
25     for(int i=1;i<=n;i++) scanf("%d",&p[i]);
26     for(int i=1;i<=n;i++)
27        for(int j=i+1;j<=n;j++)
28        {
29             if(p[i]<p[j])
30             {
31                q[i][0]++;
32             q[i][q[i][0]]=j;    
33             //printf("%d %d
",i,j);
34          }
35        }
36     for(int i=n;i>=1;i--)
37     {
38         w[i]=sum(p[i]-1);
39         ans=(ans+w[i])%P;
40         //printf("%d
",ans);
41         w[i]++;
42         for(int j=1;j<=q[i][0];j++) add(p[q[i][j]],w[q[i][j]]);
43     }
44     printf("%d
",ans);
45 }
View Code

J 夺宝奇兵

Code:zz

Thinking:KK zz

div2:由于m最大只有一千,所以从(m+1)/2到1枚举最后我需要买的物品,每次和当前答案更新。如一个人的物品比自己需要买的多或一样多,那肯定先把多余这部分的全部买走,如果发现不够,那么就在剩下的所有里面挑最便宜的;如果发现买完那些比自己大的,就已经超出当前的值了,说明不合法,就不更新答案。用multiset完成以上操作。

div1:m变成一万,不能枚举,想到二分,发现中间有一段区间虽然都是合法的,但是并不是买的物品越少钱越少(买的物品多了,强制买的物品就少,然后不足的就可以从其他物品里挑最便宜的),所以不满足二分的性质,三分好像可以做(wls吐槽好像二字),标算是线段树?还不会。

 1 //#pragma comment(linker, "/STACK:102400000,102400000")
 2 #include<iostream>
 3 #include<stdio.h>
 4 #include<stdlib.h>
 5 #include<string.h>
 6 #include<string>
 7 #include<math.h>
 8 #include<cmath>
 9 #include<time.h>
10 #include<map>
11 #include<set>
12 #include<vector>
13 #include<queue>
14 #include<algorithm>
15 #include<numeric>
16 #include<stack>
17 #include<bitset>
18 //#include<unordered_map>
19 const int inf = 0x3f3f3f3f;
20 const int maxn=100010;
21 using namespace std;
22 typedef long long ll;
23 struct s
24 {
25     long long c[1010];
26     int cnt;
27 }z[1010];
28 multiset<long long>st;
29 multiset<long long>::iterator it;
30 inline bool comp(ll a,ll b)
31 {
32     return a > b;
33 }
34 int main(void)
35 {
36     //ios::sync_with_stdio(false);
37     int n,m,i,z2,j,num,x,k;
38     long long z1,sum,ans,ss;
39     while(~scanf("%d %d",&n,&m))
40     {
41         st.clear();
42         for(i = 1;i <= n;i++)
43         {
44             z[i].cnt = 0;
45         }
46         for(i = 0;i < m;i++)
47         {
48             scanf("%lld %d",&z1,&z2);
49             z[z2].c[z[z2].cnt++] = z1;
50             st.insert(z1);
51         }
52         for(i = 1;i <= n;i++)
53         {
54             sort(z[i].c,z[i].c + z[i].cnt,comp);
55         }
56         sum = 0;
57         num = 0;
58         ans = 10000000000000;
59         for(i = m / 2 + 1;i >= 1;i--)
60         {
61             for(j = 1;j <= n;j++)
62             {
63                 if(z[j].cnt >= i)
64                 {
65                     while(z[j].cnt >= i)
66                     {
67                         num++;
68                         sum += z[j].c[z[j].cnt - 1];
69                         st.erase(st.find(z[j].c[z[j].cnt - 1]));
70                         z[j].cnt--;
71                     }
72                 }
73             }
74             if(num == i)
75             {
76                 ans = min(sum,ans);
77             }
78             else if(num < i)
79             {
80                 ss = sum;
81                 x = i - num;
82                 for(it = st.begin();it != st.end();it++)
83                 {
84                     sum += *it;
85                     x--;
86                     if(x == 0)
87                     {
88                         break;
89                     }
90                 }
91                 ans = min(sum,ans);
92                 sum = ss;
93             }
94         }
95         printf("%lld
",ans);
96     }
97     return 0;
98 }
View Code

K 星球大战

unsolved

建bfs树

训练总结:

KK: 简单dp听了pai爷的一些思路马上意会到正解。最短路看错样例,看清后秒解,然后oj的排名炸了,以为今天只能做两题,所以在梦游想题,后来发现J题数据量小,想了一下想到div2的正解,被zz优化了一下,刚好过了,自己写可能会wa一发(因为没有二分的性质,但是自己写可能会找到一个答案就直接break),以后训练要认真,很多题都是可以思考的,不到最后不要放弃。

pai爷:做题的时候缺少验证吧,不够认真对待题目,思路要清晰,想法很多,错误想法也很多。之后要大胆猜测以及大胆验证。

zz:一开局看到了B,感觉和以前做的一道题很像,想到可能是一个三重循环的dp,但是时间那一维和状态转移方程没想好,就没说,看其他题去了;后来oj出了问题,基本上没更新,题目基本上没什么思路,过了很久以后,kk跟我说了J题的暴力做法,我看了看n和m才一千,感觉行,后来在写的时候发现太暴力是要n^3的,就用multiset降到了n*n*log(n),刚好过(因为comp函数写错了wa了,哭了)。

原文地址:https://www.cnblogs.com/mountaink/p/10296396.html