李聪的博客

少年易老学难成,一寸光阴不可轻


  • 首页

  • 标签

  • 分类

  • 归档

Codeforces-965D

发表于 2018-05-21 | 分类于 ACM

思路

答案就是长度为$l$的区间最小的区间和,设其为$k$。
若答案$ans$大于$k$,因为最多只有$k$只青蛙能越过区间和为$k$的区间,一定不成立。
设$pi$为第$i$个石头的位置,因为最小的区间和为$k$,所以$p{i+k}-p_i\le l$,一定有方法过河。

向上向下取整的不等式运算

发表于 2018-05-21 | 分类于 ACM

向下取整(floor)

以下各式中除法默认为向下取整的整数除法($\lceil\quad \rceil$标注的除外)
$${a\over b}\ge c \implies \begin{cases}a\ge b\times c\\ b\le {a\over c}\end{cases} \\
{a\over b}\le c \implies \begin{cases}a\le b\times c+b-1\\ b\ge {a\over {c+1}}+1\end{cases}$$

向上取整(ceil)

对于$$\lceil {a\over b}\rceil$$可以转化成$${a+b-1}\over b$$利用向下取整的方法运算

ZOJ-4027

发表于 2018-05-07 | 分类于 ACM

题解

$dp[i][j]$表示第$i$个左括号越过第$j$个右括号
$$
dp[i][j]=\max_{k=pos[i-1]}^{j}+(sum[j]-sum[pos[i]])\times v[i]
$$

2018年湘潭大学程序设计竞赛 F-maze

发表于 2018-04-30 | 分类于 ACM

题解

由于放入队列的可能是3秒之后的,把队列换成优先队列。

2018年湘潭大学程序设计竞赛 G-又见斐波那契

发表于 2018-04-30 | 分类于 ACM

题解


$(i+1)$的各次方拆开之后都可以由$i$的个次方构造出来

ZOJ-4029

发表于 2018-04-30 | 分类于 ACM

题解

对这种带取整的式子,要考虑是不是很多情况取整后都是一样的值,此题
$$\lceil log_pa \rceil=i\qquad p^{i-1}<a\le p^i$$
由于$a\le 1e9$ $i$最大的取值为$30$,排序预处理出前缀和,对每个p二分即可

代码

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
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
const ll MAXN=5e5+100;
const ll MOD=1e9;
ll a[MAXN],sum[31][MAXN];

int main(int argc, char const *argv[]) {
// freopen("input.txt","r",stdin);
ll T_T;
cin>>T_T;
while(T_T--)
{
ll n,m;
cin>>n>>m;
a[0]=0;
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
}
sort(a,a+n+1);
for(int i=0;i<31;++i)
sum[i][0]=0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<31;++j)
sum[j][i]=sum[j][i-1]+a[i]/j;
}
ll out=0;
for(int i=1;i<=m;++i)
{
ll p;
scanf("%lld",&p);
ll pp=p;
ll lastp=0;
ll curans=0;
//printf("p==%lld\n",p);
for(int j=1;j<31;pp*=p,j++)
{
ll curp=upper_bound(a,a+n+1,pp)-a-1;
//printf(" pp==%lld curp==%lld\n",pp,curp);
curans=(curans+sum[j][curp]-sum[j][lastp])%MOD;
lastp=curp;
if(pp>=a[n])
break;
}
curans=curans*i%MOD;
out=(out+curans)%MOD;
//printf("%lld\n",out);
}
printf("%lld\n",out);
}
return 0;
}

Operation System 1

发表于 2018-04-27

Codeforces-960F

发表于 2018-04-24 | 分类于 ACM

题解

对每个节点开一个树状数组维护前缀最大值(即区间$[1,n]$的最大值)
BIT[i][j]表示存在一条指向$i$点的路径,路径中最后一个边的权值为$j$
那么对每条边$u,v,w$,查询指向$i$的路径中,最大权值不超过$w-1$,长度最大的路径的长度(即BIT[u][w-1]),再加一即为以$v$结尾,最大权值为$w$的路径的长度,同时把这个值更新到BIT[v][w]中,因为是按读入边的顺序处理的,所以可以满足边的编号递增的条件,查询的最大值即为答案。
注意直接开所有的树状数组内存是不够的,使用map就可以了

代码

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
//4 24
#include "bits/stdc++.h"
using namespace std;
#define FOP freopen("input.txt","r",stdin)
#define R index*2+1
#define Met(x,y) memset(x,y,sizeof x)
#define L index*2
typedef long long ll;
const ll MAXN =1e5+100;
const ll INF = 1e18;
const ll MOD = 1e9+7;
map<ll,ll> BIT[MAXN];
ll Lowbit(ll x) // 2^k
{
return x&(-x);
}
void update(ll id,ll i, ll x)//i点增量为x
{
i++;
while(i <MAXN)
{
// if(id==2 && i==1)
// printf("BIT[2][1]:%lld\n",BIT[2][1]);
BIT[id][i] = max(BIT[id][i],x);
// if(id==2 && i==1)
// printf("BIT[2][1]:%lld\n",BIT[2][1]);
i += Lowbit(i);
}
}
ll bmax(ll id,ll x)//区间求和 [1,x]
{
x++;
ll ret=0;
while(x>0)
{
if(BIT[id].count(x))
ret=max(ret,BIT[id][x]);
x-=Lowbit(x);
}
return ret;
}
int main(int argc, char const *argv[]) {
// freopen("input.txt","r",stdin);
ll n,m;
cin>>n>>m;
ll ans=0;
for(int i=0;i<m;++i)
{
ll u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
ll cur=bmax(u,w-1)+1;
// cout<<i<<endl;
update(v,w,cur);
ans=max(ans,cur);
}
cout<<ans<<endl;
return 0;
}

Codeforces-960E

发表于 2018-04-24 | 分类于 ACM

题解

奇数个点组成的路径才对答案有贡献
具体的解法是对每个点计算它对答案的贡献
每个点(设为$u$)对答案的贡献有两部分

  • 路径中除$u$外所有点都是在$u$的子孙节点中
    即在$u$的任意两个子树中各取一个点$v_1,v_2$,使$v_1,v_2$到$u$组成的路径同时有奇数个点或同时为偶数个点。这样从$v_1$到$v_2$,经过$u$的路径中含有奇数个点,能对答案有贡献,到$u$路径为奇数个点的做正贡献,为偶数的做负贡献。通过dfs可处理,(语言不好描述,详见代码)
    设$odd[i]$为$i$点 自身 和的子孙节点中由该点到$i$组成的路径含有奇数个点的个数
    $even[i]$为$i$点的子孙节点中由该点到$i$组成的路径含有偶数个点的个数
  • 路径中一部分是$u$的祖先节点,一部分是$u$的子孙节点
    这里有个重要结论
    设$allodd[i],alleven[i]$为图中所有点中到该点组成路径含有奇数点(偶数点)的个数
    设$1$为根节点,则
    $$allodd[1]=odd[1]\\alleven[1]=odd[1]$$
    同时若$u,v$为相邻的两个点
    $$allodd[u]=alleven[v]\\alleven[u]=allodd[v]$$
    设节点高度为$level[i],level[1]=1$
    则$$\begin{cases}
    allodd[i]=allodd[1]=odd[1],alleven[i]=alleven[i]=even[1] & level[i]\%2=1\\
    allodd[i]=alleven[1]=even[1],alleven[i]=allodd[i]=odd[1] & level[i]\%2=0\\
    \end{cases}$$
    设 $upo,upe$:该点自身和祖先节点中到该点组成的路径含奇数(偶数)个点的点的个数。
    $$upo=allodd[i]-odd[i]+1\\upe=alleven[i]-even[i]$$
    此点的贡献为
    $$
    (allodd[i]\times odd[i]-alleven[i]\times even[i])\times 2\times val[i]-val[i]
    $$
    注意当路径长度为1时,只能做一次贡献,而式子中是乘以2的,答案最后应减去一个$val[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
    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
    //4 18
    #include <iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;

    typedef long long ll;
    const ll MAXN=2e5+100;
    const ll MOD=1e9+7;
    struct Edge{
    ll from,to,nxt;
    }edge[MAXN*2];
    ll eidx;
    ll head[MAXN];
    void addedge(ll from,ll to)
    {
    edge[eidx].from=from; edge[eidx].to=to;
    edge[eidx].nxt=head[from]; head[from]=eidx++;
    }
    void einit()
    {
    eidx=0;
    memset(head,-1,sizeof head);
    }

    ll val[MAXN];
    ll o[MAXN],e[MAXN],lev[MAXN];
    ll ans;
    bool vis[MAXN];
    void dfs(ll u,ll h,ll fa){
    lev[u]=h;
    vis[u]=1;
    o[u]=e[u]=0;
    for(ll i=head[u];i!=-1;i=edge[i].nxt)
    {
    ll v=edge[i].to;
    if(vis[v]) continue;
    dfs(v,h+1,u);
    ll ta=2*(o[u]*e[v]%MOD-o[v]*e[u]%MOD)%MOD*val[u]%MOD;
    ta=(ta%MOD+MOD)%MOD;
    if(u==1)
    {
    //printf("ou %lld eu %lld ta %lld\n",o[u],e[u],ta);
    }
    ans=(ans+ta)%MOD;
    o[u]+=e[v];
    e[u]+=o[v];

    }
    o[u]++;
    }
    void init()
    {
    memset(vis,0,sizeof vis);
    }
    int main()
    {
    //freopen("input.txt","r",stdin);
    ll n;
    //while(cin>>n)
    {
    cin>>n;
    einit();
    init();
    for(int i=1;i<=n;++i){
    scanf("%lld",&val[i]);
    }
    for(int i=1;i<n;++i)
    {
    ll p1,p2;
    scanf("%lld%lld",&p1,&p2);
    addedge(p1,p2);
    addedge(p2,p1);
    }
    ans=0;
    dfs(1,1,-1);
    for(int i=1;i<=n;++i)
    {
    ll upo,upe,ta;
    if(lev[i]%2)
    {
    upo=o[1]-o[i]+1;
    upe=e[1]-e[i];
    }else{
    upo=e[1]-o[i]+1;
    upe=o[1]-e[i];
    }
    ta=2*(upo*o[i]%MOD-upe*e[i]%MOD)%MOD*val[i]%MOD;
    ta-=val[i];
    ta=(ta%MOD+MOD)%MOD;
    ans=(ans+ta)%MOD;
    }
    cout<<ans<<endl;
    }
    return 0;
    }

“今日头条杯”首届湖北省大学程序设计竞赛

发表于 2018-04-23 | 分类于 ACM

思路

把一个三角形旋转60度可以构造出一个包含题目要求三个边的三角形,可以观察出答案为原来的三个角各减60度。
正解不太好想,一个可行的思路是建立平面直角坐标系,可以随便举出等边三角形内的几个点,通过余弦定理算出三角形内的点周围的三个角大小和三个新边组成的三角形的内角,从而发现规律。

123…5

李聪

44 日志
2 分类
18 标签
© 2018 李聪
由 Hexo 强力驱动 }
|
主题 — NexT.Pisces v5.1.3
访问人数 总访问量 次