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

slove 6/11

 

A.夺宝奇兵

Code:zz

Thinking:zz

贪心即可。这条路线里,点n1和点n2肯定是相连的,接下来,点(n-1)1和点(n-1)2分别是和n1和点n2相连的,一共有两种情况,选择距离短的即可,就这样一直往前贪心。

//#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;
using namespace std;
struct s
{
    long long x1,y1,x2,y2;
}z[100010];
inline long long dis(long long x1,long long y1, long long x2,long long y2)
{
    long long z1 = x1 - x2;
    long long z2 = y1 - y2;
    if(z1 < 0)
    {
        z1 = -z1;
    }
    if(z2 < 0)
    {
        z2= -z2;
    }
    return z1 + z2;
}
int main(void)
{
    //ios::sync_with_stdio(false);
    int n,m,i;
    long long ans;
    while(~scanf("%d %d",&n,&m))
    {
        for(i = 1;i <= n;i++)
        {
            scanf("%lld %lld %lld %lld",&z[i].x1,&z[i].y1,&z[i].x2,&z[i].y2);
        }
        //printf("1111\n");
        ans = 0;
        ans += dis(z[n].x1,z[n].y1,z[n].x2,z[n].y2);
        //printf("2222 %lld\n",ans);
        for(i = n - 1;i >= 1;i--)
        {
            ans += min(dis(z[i].x1,z[i].y1,z[i + 1].x1,z[i + 1].y1) + dis(z[i].x2,z[i].y2,z[i + 1].x2,z[i + 1].y2)
            ,dis(z[i].x1,z[i].y1,z[i + 1].x2,z[i + 1].y2) + dis(z[i].x2,z[i].y2,z[i + 1].x1,z[i + 1].y1));
            /*printf("%d %lld\n",i,min(dis(z[i].x1,z[i].y1,z[i + 1].x1,z[i + 1].y1) + dis(z[i].x2,z[i].y2,z[i + 1].x2,z[i + 1].y2)
            ,dis(z[i].x1,z[i].y1,z[i + 1].x2,z[i + 1].y2) + dis(z[i].x2,z[i].y2,z[i + 1].x1,z[i + 1].y1)));*/
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

C.最小边覆盖

Code:kk

Thinking:kk zz

用树形dp找每个联通块 树的直径,树的直径大于等于4的就肯定不行,有孤立的点也不行,就这样。

然而别人都是用结论做的,每条边连上去后,边两边的点只能有一个入度大于1.

我就是个弟弟。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=200010;
const int inf=0x3f3f3f3f;
int fa[maxn],n,m;
bool vis[maxn];
int head[maxn],tot;
struct edge{
    int to,Next;
}a[maxn<<2];
void init() {
    CLR(vis,0);
    CLR(head,-1),tot=0;
}
void addv(int u,int v){
    a[++tot].to=v;
    a[tot].Next=head[u];
    head[u]=tot;
}
int u,v,maxx;
int dfs(int u,int deep,int fa)
{
//    printf("u:%d\n",u);
    int fmax=0,smax=0;
    vis[fa]=1;
    for(int i=head[u];i!=-1;i=a[i].Next)
    {
        int v=a[i].to;
        if(v==fa)continue;
        if(vis[v]==1)
        {
            maxx=inf;
            return inf;
        }
        vis[v]=1;
        int tep=dfs(v,deep+1,u);
        if(fmax==0)fmax=max(fmax,tep);
        else if(smax==0){
            smax=tep;
            if(fmax<smax)swap(fmax,smax);
         }
        else if(tep>fmax){
            smax=fmax,fmax=tep;
        }else if(tep>smax){
            smax=tep;
        }
    }
//    printf("u:%d\n",u);
//    printf("maxx:%d\n",maxx);
//    printf("fmax:%d  smax:%d\n",fmax,smax);
    maxx=max(maxx,max(fmax+smax,fmax+deep));
    if(maxx>=inf)return inf;

    return fmax+1;
}
int main() {
    while(cin>>n>>m) {
        init();
        while(m--) {
            scanf("%d%d",&u,&v);
            addv(u,v),addv(v,u);
        }
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0)
            {
                dfs(i,0,-1);
            }
            if(maxx>=3)
            {
                break;
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0){
                puts("No\n");
                return 0;
            }
        }
        if(maxx>=3){
            printf("No\n");
        }else{
            puts("Yes");
        }
    }
}
View Code

 

D.欧拉回路

题解待补。

 

F.小小马

Code:zz

Thinking:zz

马是走日字的,因此走一步后,他所在的格子的颜色和上一格肯定是不一样的,因此,如果他的起点颜色和终点颜色是一样的,那么肯定是No;如果颜色是一样的,那么只要起点和终点是互相可达的,那么是Yes,反之是No。除了3*3的棋盘和2*n的棋盘需要判断是否可达外,其他都能可达。

#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;
using namespace std;
int main(void)
{
    //ios::sync_with_stdio(false);
    int n,m,s1,s2,e1,e2;
    while(~scanf("%d %d",&n,&m))
    {
        scanf("%d %d %d %d",&s1,&s2,&e1,&e2);
        if(n > m)
        {
            int r = n;
            n = m;
            m = r;
        }
        if(n == 3 && m == 3)
        {
            if(s1 == 2 && s2 == 2 || e1 == 2 && e2 == 2)
            {
                printf("No\n");
            }
            else if((s1 + s2) % 2 == (e1 + e2) % 2)
            {
                printf("No\n");
            }
            else
            {
                printf("Yes\n");
            }
        }
        else if(n == 2)
        {
            if(s1 > 2 || e1 > 2)
            {
                int r = e1;
                e1 = e2;
                e2 = r;
                
                r = s1;
                s1 = s2;
                s2 = r;
            }
                if(e1 == s1)
                {
                    printf("No\n");
                }
                else
                {
                    if((abs(e2 - s2) - 2) % 4 == 0)
                    {
                        printf("Yes\n");
                    }
                    else
                    {
                        printf("No\n");
                    }
                }
        }
        else
        {
            if((s1 + s2) % 2 == (e1 + e2) % 2)
            {
                printf("No\n");
            }
            else
            {
                printf("Yes\n");
            }
        }
    }
    return 0;
}
View Code

 

G.置置置换

Code:pai爷

Thinking:pai爷

dp,注意到上凸和下凸的答案是一样的,从下到大枚举插入的数用组合数搞一下方案

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int p=1e9+7;
ll dp[1010],sum[1010],fac[1010],inv[1010];
ll qpow(ll a,ll b)
{
    ll ret=1;a%=p;
    while(b)
    {
        if(b&1) ret=ret*a%p;
        b/=2;a=a*a%p;
    }
    return ret;
}
ll C(ll n,ll m)
{
    if(m>n) return 0;
    return 1ll * fac[n] * inv[m] % p * inv[n - m] % p;
}
   


int main()
{
    fac[0]=1;inv[0]=1;
    for(int i=1;i<=1000;i++) 
    {
        fac[i]=1ll*fac[i-1]*i%p;
        inv[i]=qpow(fac[i],p-2);    
    }
    int n;
    scanf("%d",&n);
    dp[0]=dp[1]=1;
    sum[1]=1;
    for(int i=2;i<=n;i++)
    {
        sum[i]=0;
        for(int j=1;j<=i;j++)
        {
            sum[i]=(sum[i]+dp[j-1]*dp[i-j]%p*C(i-1,j-1)%p) %p;
        }
        dp[i]=sum[i]*qpow(2,p-2)%p;
    }
    printf("%lld\n",dp[n]);
    return 0;
}
View Code

 

H.咆咆咆哮

Code:pai爷

Thinking:pai爷

一个裸的贪心,计算一下式子枚举保留了k个物品,之后sort一下。取前k个。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
using namespace std;
struct node{
    int c,id;
}sum[1010];
int a[1010],b[1010];
long long p,q,ans=0;
int n;
bool cmp(node a,node b)
{
    return a.c>b.c;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
    }
    for(int k=1;k<=n;k++)
    {
        p=0;q=0;
        for(int i=1;i<=n;i++)
        {
            sum[i].c=a[i]-k*b[i];
            sum[i].id=i;
        }
        sort(sum+1,sum+1+n,cmp);
        for(int i=1;i<=k;i++)
        {
            p=p+a[sum[i].id];
        }
        for(int i=k+1;i<=n;i++)
        {
            q=q+b[sum[i].id];
        }
        ans=max(ans,p+1ll*q*k);
    }
    printf("%lld\n",ans);
}
View Code

 

K.两条路径

Code:kk zz

Thinking:kk

选取的两条链交点是x,所以就是选取和x有关的两条大链,而且查询次数非常少,所以就是求每个节点的四条子链的总和,如果子链为负数,则不取。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=100010;
const int inf=0x3f3f3f3f;
int n,m;
bool vis[maxn];
int head[maxn],tot;
struct edge{
    int to,Next;
}a[maxn<<2];
ll val[maxn];
ll pre[40];
void init() {
    CLR(vis,0);
    CLR(head,-1),tot=0;
    pre[1]=1;
    for(int i=2;i<=30;i++)
    {
        pre[i]=pre[i-1]*2;
    }
}
void addv(int u,int v){
    a[++tot].to=v;
    a[tot].Next=head[u];
    head[u]=tot;
}
int u,v;
ll son[maxn][4];
ll dfs(int u,int fa)
{
    for(int i=head[u];i!=-1;i=a[i].Next)
    {
        int v=a[i].to;
        if(v==fa)continue;
        ll tep=dfs(v,u);
        int flag = 1;
        for(int j=0;j<4;j++)
        {
            if(son[u][j]==0){
                son[u][j]=tep;
                flag = 0;
                break;
            }
        }
        sort(son[u],son[u]+4);
        if(son[u][0]<tep && flag){
            son[u][0]=tep;
        }
        sort(son[u],son[u]+4);
    }
//    printf("u:%d\n",u);
    return max((ll)0,son[u][3]+val[u]);
}
int main() {
    while(cin>>n) {
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&u);
            if(u>=0)
            {
                val[i]=pre[u];
            }else{
                val[i]=-pre[-u];
            }
        }
        
        for(int i=1;i<n;i++)
         {
            scanf("%d%d",&u,&v);
            addv(u,v),addv(v,u);
        }
        int q;
        cin>>q;
        while(q--)
        {
            memset(son,0,sizeof(son));
            scanf("%d",&u);
            dfs(u,-1);
            ll ans=val[u];
            for(int i=0;i<4;i++)
            {
                ans+=max((ll)0,son[u][i]);
                //printf("%lld ",son[u][i]);
            }
            //printf("\n");
            //printf("%lld\n",ans);
            int op= (ans<0);
            if(ans < 0)
            {
                ans = -ans;
            }
            int cc[100],cnt = 0;
            while(ans)
            {
                cc[cnt++] = ans %2;
                ans /= 2;
            }
            if(op)
            {
                printf("-");
            }
            if(cnt == 0)
            {
                printf("0");
            }
            for(int i = cnt - 1;i >= 0;i--)
            {
                printf("%d",cc[i]);
            }
            printf("\n");
        }

    }
}
View Code

 


 

赛后总结:

kk:今天有蛮多图的题目的,但是读c题的题意没读懂,别人用简单结论一小时不到就做出的题目,我写了树形dp求树的直径两小时后才ac,k题也是个树形dp的题,套了上一题的代码,结果int忘记改成long long了,wawawawawa,贼菜,把队友的腿都给抱断了。

pai爷:今天状态很差,那道DP题一直在想DP[i][j]表示枚举到第i个位置填了j的方案数,但是无法满足排列,想了很久才写出来,之后那道欧拉回路的时间不多了,找规律的题还是缺少一个系统的操作,最后时刻还是没有写出来。

zz:今天很快写掉了两个签到题,然后帮队友看c题,看了很久没看出来,后来发现题读错了,然后又一起看了k,做法很快就出了,代码打出来以后要出了很多bug,wa了很多次,改了很久才过,写代码还是要仔细点,不然小bug改到死。

posted @ 2019-01-23 21:45  光芒万丈小太阳  阅读(444)  评论(0编辑  收藏  举报