10.22考试反思

Contest2208 - 2019年10月多校联训B层测试13

T1 友善的D题

http://www.accoders.com/problem.php?cid=2208&pid=0

 

我们首先看到这道题,乘积最大,首先想到必定为正,然后,考虑什么情况能一定是正数,一定是偶数个负数相乘和任意个整数相乘得到的,我们考虑如何能让答案最大,负数*-1-1答案会变小,我们就可以 贪心的让先让所有的数都是负数,然后看n是不是偶数,如果不是偶数,就把这些负数里面最小的(绝对值最大的那个拿出来,变成正数)具体证明如下

 

设当前序列的最大值为a,最小值为b,现在负数积为ans

1.拿走a的时候 答案变为ans/a*(-a-1);化简得ans-ans/abs(a); 拿走b的时候同理ans-ans/abs(b),因为b的绝对值大于a的绝对值,所以拿走最大值的时候答案优,证毕

 

#include <bits/stdc++.h>
#define ll long long
#define res register 
#define MAXN 100050
#define int ll 
using namespace std;
int n,maxn,a[MAXN];
ll read()
{
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();}
    return s*w;
}
signed main()
{
    n=read();
    for(res int i=1;i<=n;i++) a[i]=read(),a[i]=a[i]>=0?-a[i]-1:a[i],maxn=max(maxn,abs(a[i]));
    if(n&1) for(res int i=1;i<=n;i++) if(a[i]==-maxn) {a[i]=-a[i]-1;break;}
    for(res int i=1;i<=n;i++) cout<<a[i]<<" ";
    return 0;
}
View Code

 

T2 友善的E题

http://www.accoders.com/problem.php?cid=2208&pid=1

预处理出0与1的前缀和,由于每次取走一个数后剩余区间会越来越短,所以先取大的(也就是原序列中为1的)再取小的,设l-r区间中1出现次数为c1,0出现的次数为c2,答案就是(2^c1-1)* 2 ^c2,预处理出2的次幂即可。

 

#include <bits/stdc++.h>
#define ll long long
#define res register 
#define MAXN 100500
#define int ll 
#define mo 1000000007
using namespace std;
int n,m,l,r,sum[MAXN],kc[MAXN];
char a[MAXN];
bool vis[MAXN];
ll read()
{
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();}
    return s*w;
}
signed main()
{
    n=read(),m=read();kc[1]=1;
    for(res int i=2;i<=100005;i++) kc[i]=((kc[i-1]<<1))%mo;
    cin>>a+1;
    for(res int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]-'0';
    while(m--){
        l=read(),r=read();
        int summ=sum[r]-sum[l-1];
        int length=r-l+1;
        int zero=length-summ;
        int one=length-zero;
        int q1=(kc[one+1]-1)*(kc[zero+1])%mo;
        printf("%lld\n",q1);
    }
    return 0;
}
View Code

 

T3. 友善的F题

http://www.accoders.com/problem.php?cid=2208&pid=2

#include <bits/stdc++.h>
#define ll long long
#define res register 
#define MAXN 1000500
#define mo 1000000007
#define int ll 
using namespace std;
int head[MAXN],n,m,x,y,z,ans[3],dp[MAXN][3],siz[MAXN];
bool vis[MAXN];
struct Node{
    int to,nxt,dis,sum[3],exist[3];
}g[MAXN];
inline void dfs(int u){
    vis[u]=1;int to=g[u].to,dis=g[u].dis;
    for(res int i=head[to];i;i=g[i].nxt){
        if(i==(u^1)) continue;
        if(!vis[i]) dfs(i);
        for(res int j=0;j<=2;j++){
            if(!g[i].exist[j]) continue;
            g[u].exist[(dis+j)%3]+=g[i].exist[j];
            g[u].sum[(dis+j)%3]=(g[u].sum[(dis+j)%3]+dis*g[i].exist[j]+g[i].sum[j])%mo;
        }    
    }
}
inline void add(int u,int v,int dis){
    static int top=1;
    g[++top].to=v;g[top].nxt=head[u];g[top].sum[dis%3]=dis;
    g[top].exist[dis%3]=1;g[top].dis=dis;head[u]=top;
}
ll read()
{
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();}
    return s*w;
}
signed main()
{
    n=read();
    for(res int i=1;i<n;i++){
        x=read()+1,y=read()+1,z=read();
        add(x,y,z),add(y,x,z);
    }
    for(res int i=2;i<n*2;i++) {
        if(!vis[i]) dfs(i);
        for(res int j=0;j<=2;j++) ans[j]=(ans[j]+g[i].sum[j])%mo;
    }
    for(res int i=0;i<=2;i++) cout<<ans[i]<<" ";
    return 0;
}
View Code

对于50%的数据,v的长度都是3的倍数,也就是说我们只统计地图1的数据就可以了,对于每个节点处理一个f数组,表示着子节点中所有的点到当前点的路径总和,对于当前的节点的每一个子节点,在递归时处理一下与之前节点的总路径长度并且加到答案里,遍历一遍树的同时就可以统计出答案。

对于100%的数据,就维护三个数组,数组之间在统计答案的同时判断一下要处理到哪个数组里去

posted @ 2019-11-12 15:56  落筱  阅读(166)  评论(0编辑  收藏  举报