Wannafly10 题解

传送门

  • 一、签到题

交了几发0%不知道是不是题目有问题
后来再改代码发现,a*(100-x)这里可能爆int,~。~

#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
ll f(ll a,int x)
{
    return a*(100-x)/100;
}

int main()
{
    int a,b,c;
    ll n;
    scanf("%lld%d%d%d",&n,&a,&b,&c);
    int aa = f(f(f(n,a),b),c);
    int bb = f(f(f(n,a),c),b);
    int cc = f(f(f(n,b),a),c);
    int dd = f(f(f(n,b),c),a);
    int ee = f(f(f(n,c),a),b);
    int ff = f(f(f(n,c),b),a);
    int ans = aa;
    //cout<<aa<<' '<<bb<<' '<<cc<<' '<<dd<<' '<<ee<<' '<<ff<<endl;
    ans = min(ans,bb);
    ans = min(ans,cc);
    ans = min(ans,dd);
    ans = min(ans,ee);
    ans = min(ans,ff);
    printf("%d\n",n-ans);
    return 0;
}
  • 二、
    暴力超时了,明天再看看别人写的
    下面是做法最简单的代码,直接用双指针往后跑,如果一个字符被匹配了就直接指针向后移动
    这里就要考虑一种情况,如果待匹配的字符是a,然后这一行的密码是a#,那么可以选择匹配或者不匹配。
    仔细思考,如果一个密码是可以表示的,那么这个密码的每一个字符都可以在一行里找出,也就是在i和i后找到,所以如果i行有匹配的字符,那么选择匹配是不会影响到后面的字符匹配的,当然选择不匹配也有可能成功,不过不用考虑。
#include <bits/stdc++.h>
using namespace std;
#define FIN freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define Fi first
#define Se second
typedef long long LL;
typedef pair<int, int>pii;

int n,m,q;
char ch[30],str[10005];
bool vis[301][30];

int main(){
    //FIN;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=0;i<n;i++){
        scanf("%s",ch);
        for(int j=0;j<m;j++){
            if(ch[j]=='#') vis[i][0]++;
            else vis[i][ch[j]-'a'+1]++;
        }
    }
    while(q--){
        scanf("%s",str);
        int len=strlen(str);
        if(len>n)
            puts("NO");
        else{
            int now=0;
            for(int i=0;i<n&&now<len;i++){
                if(vis[i][str[now]-'a'+1]){
                    now++;
                }
            }
            printf("%s\n",now<len?"NO":"YES");
        }
    }
    return 0;
}

这样看来这道题目难度还远远不及普通的包含通配符的字符串匹配。

  • 三、题目
    压缩版题意是:给一颗无根树,Q个查询,每次查询 先轰炸这个点,然后输出这个点目前为止 被轰炸的次数,(每个点被轰炸时,会波及距离2以内的点,也就是 点点之间的边数<=2的点)
    • 开始题目没说清楚轰炸结束是一次轰炸结束,并且数据比较大750000,我就把所有轰炸离线了,最后查询。
      思路是对于每个点,向上更新一次fa[i]和fa[fa[i]]的信息,最后再按照查询输出。这样复杂度是o(n)

#define mem(x) memset(x,0,sizeof(x))


using namespace std;
const int N = 750010;
int dp[N][2];//[0]记录总贡献,[1]记录子节点的贡献


int A[N];//记录[i]的直接轰炸次数
vector<int> tree[N];

void init()
{
    mem(dp);mem(fa);mem(vis);
}
/*
 *搜索得到father数组
 */

int fa[N];
bool vis[N];
void dfs(int u)
{
    vis[u]=true;
    for(int i=0;i<tree[u].size();i++)
    {
        int v = tree[u][i];
        if(!vis[v])
        {
            vis[v]=true;
            dfs(v);
            fa[v]=u;
        }
    }
}

int n,q;

int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        init();
        int a,b;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            tree[a].push_back(b);
            tree[b].push_back(a);
        }
        for(int i=1;i<=q;i++){
            scanf("%d",&x);
            A[x]++;
        }
        A[0]=0;
        dfs(1);//默认1是根,不影响结果。
        for(int i=2;i<=n;i++)
        {
            dp[fa[i]][1]+=A[i];//记录子节点的贡献

            //互相贡献
            dp[fa[i]][0]+=A[i];
            dp[fa[fa[i]]][0]+=A[i];
            dp[i][0]+=A[fa[fa[i]]]+A[fa[i]];
        }
        for(int i=1;i<=n;i++)//还要加上所有兄弟节点以及自己的轰炸次数
        {
            dp[i][0]+=dp[fa[i]][1];
        }
        //最后按照查询输出即可
    }
    return 0;
}

写出来发现样例都过不了,
那按照题意,然后只能每次查询遍历所有邻接点。。
那要是数据极端一点就会超时啊
然后改了一下,提交竟然过了~~

如果是边查询边输出的话,就当做图来思考,dp数组记录当前邻接点的和,
x和v相邻
ans = A[x]+sigma(dp[v]-A[x] + A[v]) 自己的次数+ 距离为2 + 距离为1的次数

using namespace std;
const int N = 750010;
int dp[N];
int A[N];
vector<int> tree[N];

void init()
{
    mem(dp);
    mem(A);
    for(int i=0;i<N;i++)
        tree[i].clear();
}

int n,q;

int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        init();
        int a,b;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            tree[a].push_back(b);
            tree[b].push_back(a);
        }
        A[0]=0;
        for(int i=1;i<=q;i++)
        {
            int x;
            scanf("%d",&x);
            A[x]++;
            int ans=A[x];//自己的次数
            for(int j=0;j<tree[x].size();j++)
            {
                int v=tree[x][j];
                dp[v]++;//边为1的点加一次
                ans+=dp[v]+A[v]-A[x];//加上邻结点的邻结点的次数,记得减去自己。
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

  • 可能是一道线段树的题,明天补
    1e5的数据量来看,线段树无疑,然后维护的是最大有效(连续)子区间和,就想起了以前写的,分治法解决最大连续子区间和
    不过问题是,在在线处理的时候,如何对分治进行结合线段树的合并。
    • 原博客的处理是遍历一遍n进行合并,一次处理的复杂度就是n,加起来就是m*logn * n,这道题明显不行。
      那其实上述合并的操作就是,把左区间的右连续+有区间的左连续和 合并(如果左右区间连续)
    • 那维护的时候就要同时记录lsum,和rsum
    • ssum好说 : ssum[rt]=左右连续? rsum[rt<<1]+lsum[rt<<1|1] : max(ssum[rt<<1],ssum[rt<<1|1])
    • 然后问题就变成了,怎么更新lsum和rsum
    • 我们先看lsum的定义,左端点连续区间和。
    • 那至少得包括左端点,不是左区间的lsum,就是左区间的全部加上右区间的lsum
    • lsum[rt] = max(lsum[rt<<1],sum[rt<<1]+lsum[rt<<1|1]) 当然后者的前提还是连续
    • 那至此我们要维护4个值,sum,ssum,lsum,rsum
    • 判断是否左右是否连续就 把 m 和m+1拿来判断一下就ok了

到此这个问题应该是出来了
先来一发

//第一次写这种肝了1个多小时,注释里写一些写题目
#include<bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;
int n,m;
const int N=1e5;
int A[N];
const int INF = 0x3ffffff;
typedef long long ll;
ll ssum[N<<2];
ll sum[N<<2];
ll lsum[N<<2];
ll rsum[N<<2];
//查询函数需要返回结构体才行
//这里可以把上面4个东西写入结构体,我这里开始没发现,所以就没改,就是用一个临时的node存放
typedef struct node{
    ll sum,lsum,rsum,ssum;
    node()
    {
        sum=lsum=rsum=ssum = -INF;
    }
}Node;


//判断左右是否连续
bool isok(int a,int b)
{
    //a^b 奇奇偶偶2进制第一位都是0
    a = a>0?a:-a;
    b = b>0?b:-b;
    return (a^b)&1 ;
}

//这里参考上面的解释
void PushUp(bool ok,int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    ssum[rt]=max(ssum[rt<<1],ssum[rt<<1|1]);
    lsum[rt]=lsum[rt<<1];
    rsum[rt]=rsum[rt<<1|1];
    if(ok)
    {
        lsum[rt]=max(lsum[rt],sum[rt<<1]+lsum[rt<<1|1]);
        rsum[rt]=max(rsum[rt],rsum[rt<<1]+sum[rt<<1|1]);
        ssum[rt]=max(ssum[rt],rsum[rt<<1]+lsum[rt<<1|1]);
    }

}

//分治都要考虑是否连续
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=lsum[rt]=rsum[rt]=ssum[rt]=A[l];
    }
    else
    {
        int m=l+(r-l)/2;
        build(lson);
        build(rson);
        bool fg = isok(A[m],A[m+1]);
        PushUp(fg,rt);
    }
}

void update(int L,int C,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=lsum[rt]=rsum[rt]=ssum[rt]=C;
    }
    else
    {
        int m=l+(r-l)/2;
        if(L<=m)update(L,C,lson);
        else update(L,C,rson);
        bool fg = isok(A[m],A[m+1]);
        PushUp(fg,rt);
    }
}

//由于查询没有pushup,所以要把4个数据写入node中来作为返回值
Node query(int L,int R,int l,int r,int rt)
{
    Node ret;
    if(L<=l && r<=R)
    {
        ret.sum = sum[rt];
        ret.lsum = lsum[rt];
        ret.rsum = rsum[rt];
        ret.ssum = ssum[rt];
        return ret;
    }
    else
    {
        int m=l+(r-l)/2;
        Node left,right;
        bool fg = isok(A[m],A[m+1]);
        if(L<=m)
            left=query(L,R,lson);
        if(R>=m+1)
            right=query(L,R,rson);
        ret.sum=left.sum+right.sum;
        ret.ssum = max(left.ssum,right.ssum);
        ret.lsum = left.lsum;
        ret.rsum = right.rsum;
        if(fg)
        {
            ret.ssum = max(ret.ssum,left.rsum+right.lsum);
            ret.lsum = max(ret.lsum,left.sum+right.lsum);
            ret.rsum = max(ret.rsum,left.rsum+right.sum);
        }
        return ret;
    }
}


int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)scanf("%d",A+i);
        int op,x,y;
        build(1,n,1);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&op,&x,&y);
            cout<<op<<' '<<x<<' '<<y<<endl;
            if(op)update(x,y,1,n,1);
            else printf("%lld\n",query(x,y,1,n,1).ssum);
        }
    }
    return 0;
}

然后这个程序只过了样例,肝不动了,回头再看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值