cf Round 607

A.Chain Reaction(DP+二分)

题意:一排有n个灯塔,每个灯塔给出坐标xi和力量yi,
每次从最右边依次点亮灯塔,每点亮一个灯塔,它左边的距离它yi范围内的灯塔将受到损坏。
现在允许在最右边>max(xi)处添加一座力量值yi的灯塔。
问最少只会损坏多少灯塔。

分析:因为我们是从右边点亮灯塔的,所以我们点亮我们添加的那座灯塔只会损坏连续个灯塔。最后就相当于从末尾的某一个灯塔开始点亮。
我们令dp[i]表示点亮第i座灯塔最少损坏多少灯塔。
于是我们对于每个dp[i],二分可以得到他前面的前驱。
所以dp[i]=dp[j]+1(j<i).
注意题目给出的灯塔并不是有序的,需要排序一遍。

# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <math.h>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define MAXN 100005
# define eps 1e-5
# define MAXM 1000005
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
typedef unsigned long long ULL;
int _MAX(int a, int b){return a>b?a:b;}
int _MIN(int a, int b){return a>b?b:a;}
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())=='-') flag=1;
    else if(ch>='0'&&ch<='9') res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')  res=res*10+(ch-'0');
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar('-'); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+'0');
}

int a[100005][2], sum[100005];

int comp(const void * b, const void * c){return *(int *)b-*(int *)c;}
int main ()
{
    int n;
    scanf("%d",&n);
    FOR(i,1,n) scanf("%d%d",&a[i][0],&a[i][1]);
    qsort(a+1,n,sizeof(a[1]),comp);
    sum[1]=1;
    FOR(i,2,n) {
        int l=0, r=i, mid;
        while (l<r) {
            mid=(l+r)>>1;
            if (l==mid) break;
            if (a[mid][0]<a[i][0]-a[i][1]) l=mid;
            else r=mid;
        }
        sum[i]=1+sum[l];
    }
    int ans=0;
    FOR(i,1,n) ans=max(ans,sum[i]);
    printf("%d
",n-ans);
    return 0;
}
View Code

B.Zuma(区间DP)

给出一串n个数字(n<=500).每次操作可以消掉一个回文串。然后补齐。
问最少需要操作多少次。

姿势很优雅的区间DP;
很容易想到dp[l][r]表示消掉[l,r]的回文串最少需要多少步。
但是在转移的过程中GG了,假如它先消中间的某一部分,然后再对齐再消,这样咋办。
于是我码了一个暴力判断字符串是否回文。结果TLE了。
仔细一想,因为1个数字显然是回文串。
对于[l,r]区间,我们枚举[l+1,r]内的一个k使得a[k]==a[l],这时候我们可以先消[l+1,k-1].
再消a[l]和a[k],我们注意到消[l+1,k-1]的最后一步一定会变成一个回文串,
因为a[l]==a[k], 所以我们消a[l,k]的步数就等于a[l+1,k-1].
即dp[l][k]==dp[l+1][k-1].
所以状态转移方程就是dp[l][r]=min(dp[l+1][r]+1,dp[l+1][k-1]+dp[k+1][r])(a[k]==a[l])
最后判断一下边界就行了。

# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <math.h>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define MAXN 100005
# define eps 1e-5
# define MAXM 1000005
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
typedef unsigned long long ULL;
int _MAX(int a, int b){return a>b?a:b;}
int _MIN(int a, int b){return a>b?b:a;}
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())=='-') flag=1;
    else if(ch>='0'&&ch<='9') res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')  res=res*10+(ch-'0');
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar('-'); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+'0');
}

int n, a[505], dp[505][505];

int dfs(int l, int r)
{
    if (~dp[l][r]) return dp[l][r];
    if (l>r) return 1;
    if (l==r) return dp[l][r]=1;
    int ans=INF;
    ans=min(ans,dfs(l+1,r)+1);
    FOR(i,l+1,r) {
        if (a[i]==a[l]) {
            if (i==r) ans=min(ans,dfs(l+1,i-1));
            else if (i==l+1) ans=min(ans,dfs(i+1,r)+1);
            else ans=min(ans,dfs(l+1,i-1)+dfs(i+1,r));
        }
    }
    return dp[l][r]=ans;
}
int main ()
{
    mem(dp,-1);
    scanf("%d",&n);
    FOR(i,1,n) scanf("%d",a+i);
    dfs(1,n);
    printf("%d
",dp[1][n]);
    return 0;
}
View Code

C.Marbles(脑洞+hash)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int MI = ~0u>>1;

int main()
{
    int n;
    string s1,s2;
    cin>>n>>s1>>s2;
    n--;
    for(int i=0;i<n;i++)
    {
        switch(s2[i])
        {
            case 'N':s2[i]='S';break;
            case 'S':s2[i]='N';break;
            case 'W':s2[i]='E';break;
            case 'E':s2[i]='W';break;
        }
    }
    ULL v1=0,v2=0;
    const ULL seed=131;
    ULL wt=1;
    for(int i=n-1;i>=0;i--,wt*=131)
    {
        v1=v1+(s1[i]-'a')*wt;
        v2=v2*seed+s2[i]-'a';
        if(v1==v2)
        {
            printf("NO");
            return 0;
        }
    }
    printf("YES");
    return 0;
}
View Code

D.Power Tree(线段树维护dfn+逆元)

分析: 我们可以算出每一个节点对根节点的贡献因子mi。
即di是节点i的儿子节点数+1.
实际上mi就是从i到根节点的简单路径上的di的乘积。
当新加入一个节点u到节点p时。我们观察可以发现,仅仅改变了p和p的子树的mi。
显然是mi=mi*(dp+1)/dp. mu=mp。
我们需要一个数据结构可以 对一个区间进行乘,更新一个值,以及求一个区间的总和。
我们用线段树维护dfn序。
那么一个节点的子树就是一段连续的区间。
我们求一个节点的贡献时。显然就是sum同时除以他们的公共路径的mi。
由于乘法是一个浮点数。我们先累乘所有的分子,再累乘所有的分母,最后我们用乘法逆元
可以求出取模。

代码量有点受不了。。。

#include <cstdio>
#include <vector>
#include <algorithm>
#pragma comment(linker, "/STACK:16000000")
using namespace std;

typedef long long ll;

const int mod = 1000000007;
const int Maxn = 200005;
const int Maxm = 1048576;

int n, q;
int val[Maxn];
int a[Maxn], b[Maxn], c[Maxn];
vector <int> neigh[Maxn];
int ch[Maxn];
int P[Maxn], cur, L[Maxn], R[Maxn];
int mult[Maxm];
int fl[Maxm], sum[Maxm];

void Traverse(int v)
{
    L[v] = ++cur;
    for (int i = 0; i < neigh[v].size(); i++)
        Traverse(neigh[v][i]);
    R[v] = cur;
}

void createMult(int v, int l, int r)
{
    mult[v] = 1;
    if (l < r) {
        int m = l + r >> 1;
        createMult(2 * v, l, m); createMult(2 * v + 1, m + 1, r);
    }
}

void updMult(int v, int l, int r, int a, int b, int val)
{
    if (l == a && r == b) mult[v] = ll(mult[v]) * val % mod;
    else {
        int m = l + r >> 1;
        if (a <= m) updMult(2 * v, l, m, a, min(m, b), val);
        if (m + 1 <= b) updMult(2 * v + 1, m + 1, r, max(m + 1, a), b, val);
    }
}

int getMult(int v, int l, int r, int x)
{
    int res = mult[v];
    if (l < r) {
        int m = l + r >> 1;
        if (x <= m) res = ll(res) * getMult(2 * v, l, m, x) % mod;
        else res = ll(res) * getMult(2 * v + 1, m + 1, r, x) % mod;
    }
    return res;
}

void Union(int v)
{
    sum[v] = (sum[2 * v] + sum[2 * v + 1]) % mod;
}

void downOn(int v, int val)
{
    fl[v] = ll(fl[v]) * val % mod;
    sum[v] = ll(sum[v]) * val % mod;
}

void Down(int v)
{
    if (fl[v] != 1) {
        downOn(2 * v, fl[v]); downOn(2 * v + 1, fl[v]);
        fl[v] = 1;
    }
}

void createSum(int v, int l, int r)
{
    fl[v] = 1;
    if (l == r) sum[v] = val[l];
    else {
        int m = l + r >> 1;
        createSum(2 * v, l, m); createSum(2 * v + 1, m + 1, r);
        Union(v);
    }
}

int getSum(int v, int l, int r, int a, int b)
{
    if (l == a && r == b) return sum[v];
    else {
        Down(v);
        int m = l + r >> 1;
        int res = 0;
        if (a <= m) res = (res + getSum(2 * v, l, m, a, min(m, b))) % mod;
        if (m + 1 <= b) res = (res + getSum(2 * v + 1, m + 1, r, max(m + 1, a), b)) % mod;
        return res;
    }
}

void multSum(int v, int l, int r, int a, int b, int val)
{
    if (l == a && r == b) downOn(v, val);
    else {
        Down(v);
        int m = l + r >> 1;
        if (a <= m) multSum(2 * v, l, m, a, min(m, b), val);
        if (m + 1 <= b) multSum(2 * v + 1, m + 1, r, max(m + 1, a), b, val);
        Union(v);
    }
}

void addSum(int v, int l, int r, int x, int val)
{
    if (l == r) sum[v] = val;
    else {
        Down(v);
        int m = l + r >> 1;
        if (x <= m) addSum(2 * v, l, m, x, val);
        else addSum(2 * v + 1, m + 1, r, x, val);
        Union(v);
    }
}

int Inv(int a)
{
    int p = mod - 2;
    int res = 1;
    while (p) {
        if (p & 1) res = ll(res) * a % mod;
        p >>= 1; a = ll(a) * a % mod;
    }
    return res;
}

int main()
{
    n = 1; scanf("%d %d", &val[n], &q);
    for (int i = 0; i < q; i++) {
        scanf("%d %d", &a[i], &b[i]);
        if (a[i] == 1) {
            scanf("%d", &c[i]);
            n++; P[n] = b[i];
            neigh[b[i]].push_back(n);
        }
    }
    Traverse(1);
    createMult(1, 1, R[1]);
    createSum(1, 1, R[1]);
    n = 1;
    for (int i = 0; i < q; i++)
        if (a[i] == 1) {
            n++;
            ch[b[i]]++;
            int tomult = ll(ch[b[i]] + 1) * Inv(ch[b[i]]) % mod;
            updMult(1, 1, R[1], L[b[i]], R[b[i]], tomult);
            multSum(1, 1, R[1], L[b[i]], R[b[i]], tomult);
            val[n] = c[i];
            tomult = getMult(1, 1, R[1], L[n]);
            addSum(1, 1, R[1], L[n], ll(tomult) * val[n] % mod);
        } else {
            int res = getSum(1, 1, R[1], L[b[i]], R[b[i]]);
            if (P[b[i]]) {
                int tomult = Inv(getMult(1, 1, R[1], L[P[b[i]]]));
                res = ll(res) * tomult % mod;
            }
            printf("%d
", res);
        }
    return 0;
}
View Code

E.Cross Sum(待填坑)

原文地址:https://www.cnblogs.com/lishiyao/p/6255967.html