Processing math: 0%

2016-2017 National Taiwan University World Final Team Selection Contest (Codeforces Gym) 部分题解

 

D

考虑每个点被删除时其他点对它的贡献,然后发现要求出距离为1~k的点对有多少个。

树分治+FFT。分治时把所有点放一起做一遍FFT,然后减去把每棵子树单独做FFT求出来的值。

复杂度nlog^2n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include<bits/stdc++.h>
#define N 270000
#define pi acos(-1)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int p = 1000000007;
int pw(int x,int y)
{
    int lst=1;
    while(y)
    {
        if(y&1)lst=1LL*lst*x%p;
        y>>=1;
        x=1LL*x*x%p;
    }
    return lst;
}
int head[N],ver[2*N],nxt[2*N],tot;
void add(int a,int b)
{
    tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;return ;
}
struct E
{
    double x,y;
    E(){;}
    E(double _x,double _y)
    {
        x=_x;y=_y;
    }
    friend E operator + (E &a,E &b)
    {
        return E(a.x+b.x,a.y+b.y);
    }
    friend E operator - (E &a,E &b)
    {
        return E(a.x-b.x,a.y-b.y);
    }
    friend E operator * (E &a,E &b)
    {
        return E(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
    }
}a[N],b[N],c[N];
int R[N];
ll ans[N];
void fft(E *a,int f,int n)
{
    for(int i=0;i<n;i++)if(i>R[i])swap(a[i],a[R[i]]);
    for(int i=1;i<n;i<<=1)
    {
        E wn(cos(pi/i),f*sin(pi/i));
        for(int j=0;j<n;j+=(i<<1))
        {
            E w(1,0);
            for(int k=0;k<i;k++,w=w*wn)
            {
                E x=a[j+k],y=a[j+k+i]*w;
                a[j+k]=x+y;a[j+k+i]=x-y;
            }
        }
    }
    if(f==-1)
    {
        for(int i=0;i<n;i++)a[i].x/=n;
    }
    return ;
}
void FFT(int *sa,int m,int f)
{
    int l=0,n=1;
    while(n<=2*m)n<<=1,l++;
    for(int i=0;i<n;i++)
    {
        a[i].y=a[i].x=b[i].x=b[i].y=0;
        if(i<=m)a[i].x=b[i].x=sa[i];
    }
    for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(l-1));
    fft(a,1,n);fft(b,1,n);
    for(int i=0;i<n;i++)c[i]=a[i]*b[i];
    fft(c,-1,n);
    for(int i=2;i<n;i++)
    {
        ll tmp=(ll)(c[i].x+0.5);
        ans[i-1]+=f*tmp;
    }
    return ;
}
int size[N],mn,id,sum,v[N];
int n;
void dfs(int x,int f)
{
    int mx=0;
    size[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        if(v[ver[i]]||ver[i]==f)continue;
        dfs(ver[i],x);
        size[x]+=size[ver[i]];
        mx=max(mx,size[ver[i]]);
    }mx=max(mx,sum-size[x]);
    if(mx<mn)
    {
        mn=mx;
        id=x;
    }
    return ;
}
int now[N],nw[N];
int mxx=0;
void dffs(int x,int f,int dp)
{
    size[x]=1;ans[dp]+=2;now[dp]++,nw[dp]++;
    if(dp>mxx)mxx=dp;
    for(int i=head[x];i;i=nxt[i])
    {
        if(v[ver[i]]||ver[i]==f)continue;
        dffs(ver[i],x,dp+1);
        size[x]+=size[ver[i]];
    }
    return ;
}
void solve(int x)
{
    sum=size[x];mn=inf;id=x;
    dfs(x,-1);
    x=id;
    v[x]=1;size[x]=1;ans[1]++;
    for(int i=0;i<=sum;i++)now[i]=0;
    int mx=0;
    for(int i=head[x];i;i=nxt[i])
    {
        if(v[ver[i]])continue;
        mxx=0;
        dffs(ver[i],x,2);
        size[x]+=size[ver[i]];
        FFT(nw,mxx,-1);
        mx=max(mx,mxx);
        for(int j=0;j<=mxx;j++)nw[j]=0;
    }
    if(mx)FFT(now,mx,1);
    for(int i=head[x];i;i=nxt[i])
    {
        if(!v[ver[i]])solve(ver[i]);
    }
}
int main()
{
    scanf("%d",&n);
    int t1,t2;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&t1,&t2);
        add(t1,t2);add(t2,t1);
    }
    size[1]=n;
    solve(1);
    ll as=0;
    for(int i=1;i<=n;i++)
    {
        ans[i]%=p;
        as+=ans[i]*pw(i,p-2)%p;
    }
    as%=p;
    for(int i=2;i<=n;i++)
    {
        as=as*i%p;
    }
    printf("%I64d\n",as);
    return 0;
}

 

E

把每条线段看成二维平面上的一个点。

相当于求从(0,0)点到(n+1,n+1)的一条权值和最小的一条路径,且相邻两个点之间不能有其他点。

CDQ分治+单调栈+线段树

和bzoj 4273很像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include<bits/stdc++.h>
#define N 100055
#define inf 2147483647
#define ls x<<1,l,mid
#define rs x<<1|1,mid+1,r
using namespace std;
int n;
int p[N],v[N];
int a[N*4];
void gai(int x,int l,int r,int pos,int z)
{
    if(l==r)
    {
        a[x]=z;return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)gai(ls,pos,z);
    else gai(rs,pos,z);
    a[x]=min(a[x<<1],a[x<<1|1]);
    return ;
}
int qur(int x,int l,int r,int ll,int rr)
{
    if(l>=ll&&r<=rr)return a[x];
    int mid=(l+r)>>1;
    if(ll>mid)return qur(rs,ll,rr);
    if(rr<=mid)return qur(ls,ll,rr);
    return min(qur(rs,ll,rr),qur(ls,ll,rr));
}
int st1[N],top1,st2[N],top2;
int q1[N],cnt1,q2[N],cnt2,f[N];
bool cmp(int x,int y)
{
    return p[x]<p[y];
}
void solve(int l,int r)
{
    if(l==r)return ;
    int mid=(l+r)>>1;
    solve(l,mid);
    top1=top2=cnt1=cnt2=0;
    for(int i=l;i<=mid;i++)q1[++cnt1]=i;
    for(int i=mid+1;i<=r;i++)q2[++cnt2]=i;
    sort(q1+1,q1+cnt1+1,cmp);sort(q2+1,q2+cnt2+1,cmp);
    int pt=1;
    for(int i=1;i<=cnt2;i++)
    {
        while(pt<=cnt1&&p[q1[pt]]<=p[q2[i]])
        {
            while(top1&&st1[top1]<q1[pt])
            {
                gai(1,1,n,p[st1[top1]],inf);
                top1--;
            }
            gai(1,1,n,p[q1[pt]],f[q1[pt]]+v[q1[pt]]);
            st1[++top1]=q1[pt];
            pt++;
        }
        while(top2&&st2[top2]>q2[i])top2--;
        int tmp=0;
        if(top2)tmp=p[st2[top2]]+1;
        f[q2[i]]=min(f[q2[i]],qur(1,1,n,tmp,p[q2[i]]));
        st2[++top2]=q2[i];
    }
    while(top1)gai(1,1,n,p[st1[top1]],inf),top1--;
    solve(mid+1,r);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&p[i]);
    for(int i=1;i<=n;i++)scanf("%d",&v[i]);
    for(int i=1;i<=4*(n+1);i++)a[i]=inf;
    p[n+1]=n+1;p[0]=0;
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    solve(0,n+1);
    printf("%d\n",f[n+1]);
    return 0;
}

  

H

按极角排序,然后用一条线扫过去。

然后狂WA第5个点,去膜了下Claris的代码,发现有些细节写错了。。。

当一个点左右两个点极角比它小的时候,那么答案会加一,否则会减一。

如果连续一条直线上极角都相等,只拿端点算贡献。

然后这些点分为两类,假设现在答案要加一。

如果这个点在右下(意会一下),那么只有扫描线扫过这个点时答案才会加一。

如果在左上,那么扫到这个点时答案已经加了一。

讨论一下,具体看代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
int n;
struct node
{
    int x,y;
    node(){;}
    node(int _x,int _y)
    {
        x=_x;y=_y;
    }
    friend node operator - (const node &aa,const node &bb)
    {
        return node(aa.x-bb.x,aa.y-bb.y);
    }
}a[N];
int p[N];
ll cj(const node &aa,const node &bb)
{
    return 1LL*aa.x*bb.y-1LL*aa.y*bb.x;
}
bool in[N],ok[N];
bool cmp(int x,int y)
{
    return cj(a[x],a[y])>0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);
    a[0]=a[n];a[n+1]=a[1];
    for(int i=1;i<=n;i++)p[i]=i;
    sort(p+1,p+n+1,cmp);
    int sum=0,ans=0;
    for(int i=1;i<=n;)
    {
        node as=a[p[i]];
        int tmp=0;
        for(;i<=n&&cj(as,a[p[i]])==0;i++)
        {
            if(cj(a[p[i]+1],a[p[i]])<0&&cj(a[p[i]-1],a[p[i]])<=0)
            {
                if(cj(a[p[i]-1]-a[p[i]],a[p[i]+1]-a[p[i]])>0)sum++;
                else tmp++;
            }
            if(cj(a[p[i]+1],a[p[i]])>0&&cj(a[p[i]-1],a[p[i]])>=0)
            {
                if(cj(a[p[i]-1]-a[p[i]],a[p[i]+1]-a[p[i]])<0)sum--;
                else tmp--;
            }
        }
        ans=max(ans,sum);
        sum+=tmp;
        ans=max(ans,sum);
    }
    printf("%d\n",ans+1);
     
    return 0;
}

  

I

傻逼题,一个子树要么给上边提供一个两个叶子的小子树,要么是一个叶子或零个,其他的只能直接配对,画画图大力分类讨论+贪心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n;
int head[N],ver[N*2],nxt[N*2],tot;
void add(int a,int b)
{
    tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;return ;
}
int du[N],root;
int ans;
int dfs(int x,int f)
{
    int t1=0,t2=0;
    int cnt=0;
    for(int i=head[x];i;i=nxt[i])
    {
        if(ver[i]==f)continue;
        cnt++;
        int tmp=dfs(ver[i],x);
        if(tmp==1)t1++;
        else if(tmp==2)t2++;
    }
    if(!cnt)return 1;
    while(t2>=2)t2-=2,ans++;
    while(t1>=3)t1-=2,ans++;
    if(!t1)
    {
        if(t2==1)return 2;
        return 0;
    }
    if(t1==1)
    {
        if(t2==1)return 2;
        return 1;
    }
    else
    {
        if(!t2)return 2;
        else if(t2==1){ans++;return 1;}
        else {ans++;return 2;}
    }
}
int main()
{
    scanf("%d",&n);
    if(n==2)
    {
        puts("1");
        return 0;
    }
    int t1,t2;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&t1,&t2);
        add(t1,t2);add(t2,t1);
        du[t1]++;du[t2]++; 
    }
    for(int i=1;i<=n;i++)if(du[i]>1)root=i;
    int tmp=dfs(root,-1);
    if(tmp==2)ans++;
    printf("%d\n",ans);
    return 0;
}

  

J

先假设一共有无数个0。

枚举右端点,枚举左端点,然后把中间的1去掉,剩下的操作往里边塞0。

列下式子发现左端点单调,可以用单调队列优化。

最后把ans和0的个数取个min。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
char s[N];
int n,mx,sum[N];
int q[N];
void solve(int x)
{
    int ha=1,ta=1;
    q[1]=0;int ans=0;
    for(int i=1;i<=n;i++)
    {
        while(ta>=ha&&sum[i]-sum[q[ha]]>x)ha++;
        if(ta>=ha)
        {
            ans=max(ans,i-2*sum[i]+x+2*sum[q[ha]]-q[ha]);
        }
        while(ta>=ha&&2*sum[q[ta]]-q[ta]<=2*sum[i]-i)ta--;
        q[++ta]=i;
    }
    printf("%d\n",min(ans,mx));
    return ;
}
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        sum[i]=sum[i-1];
        if(s[i]=='1')sum[i]++;
    }
    for(int i=1;i<=n;i++)if(s[i]=='0')mx++;
    int q;scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int cnt;scanf("%d",&cnt);
        solve(cnt);
    }
    return 0;
}

 

  

 

 

posted @   SD_le  阅读(699)  评论(0编辑  收藏  举报
重置按钮
点击右上角即可分享
微信分享提示