星星之火

[jzoj NOIP2018模拟 11.01]

很庆幸打了这场模拟赛,因为这一场爆零

好像上次纪中的某场比赛我也出现了同样的问题,光是计算时间复杂度而忘记了空间的限制。想必是比上次惨的,考场上就写了两题而这两题都因为MLE爆零了。而且我T2还码了7k自信满满可以A掉,但实际上因为常数太大解决掉MLE的锅之后也只有50分,以后大概要失去在考场上码300多行的勇气了。

不多讲了写题解吧


T1:乘 

题目链接:

https://jzoj.net/senior/#contest/show/2545/0

题目:

题解:

考场上欧拉定理玩来玩去就是想不出,没想到正解居然如此...easy

我们要计算$a^b$,而$b<=1e12$。我们令$N=1e6,A=b%N,B=b/N$,显然答案就是$a^Aa^{BN}$。

于是我们可以预处理$a$的次幂和$a^N$的次幂,$O(1)$查询就是了

#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;

const int N=1e6;
ll a,p,q,k,b0,l,m,c;
ll pin[N+15],bin[N+15];
ll qpow(ll a,ll b)
{
    ll re=1;
    for (;b;b>>=1,a=a*a%p) if (b&1) re=re*a%p;
    return re;
}
int main()
{
    freopen("pow.in","r",stdin);
    freopen("pow.out","w",stdout);
    scanf("%lld%lld%lld%lld",&a,&p,&q,&k);
    scanf("%lld%lld%lld%lld",&b0,&l,&m,&c);
    pin[0]=1;
    for (int i=1;i<=N;i++) pin[i]=pin[i-1]*a%p;
    ll aa=qpow(a,N);
    bin[0]=1;
    for (int i=1;i<=N;i++) bin[i]=bin[i-1]*aa%p;
    ll ans=0;
    for (int i=1;i<=q;i++)
    {
        b0=(m*b0+c)%l;
        ll A=b0/N,B=b0%N;
        ans=ans^(bin[A]*pin[B]%p);
        if (i%k==0) printf("%lld\n",ans);
    }
    return 0;
}
View Code

T2:树 

题目链接:

https://jzoj.net/senior/#contest/show/2545/1

题目:

题解:

啧一看到按位与就陷入了自带常数32的怪圈,我打包票如果时间开成5s我的考场代码可以A掉

然后并不需要这个常数

发现按位与只会让数值变小,每个元素只会变小$log$次。我们只需要维护区间的或来判断当前与的值是否会对区间内的某个元素产生影响,如果有的话就暴力递归下去就是了

那个期望什么的就是把平方和的算式拆开,发现只需要维护一下区间和和区间平方和就可以了。由于上面那个操作是单点修改,一切都是那么的和谐

时间复杂度$O(nlog^2n)$

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;

const int N=1e5+15;
const ll mo=998244353;
int n;
ll a[N],t[N<<2],sf[N<<2],s[N<<2];
inline int read(){
    char ch=getchar();int s=0,f=1;
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
#define mid ((l+r)>>1)
void pushup(int o)
{
    t[o]=t[o<<1]|t[o<<1|1];
    s[o]=s[o<<1]+s[o<<1|1]; 
    sf[o]=(sf[o<<1]+sf[o<<1|1])%mo;
}
void build(int o,int l,int r)
{
    if (l==r)
    {
        t[o]=a[l];
        s[o]=a[l];
        sf[o]=a[l]*a[l]%mo;
        return;
    }
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}
bool ck(ll a,ll b)
{
    for (int j=0;j<32;j++) if ((!(b>>j&1))&&(a>>j&1)) return 0;
    return 1;
}
void update(int o,int l,int r,int x,int y,ll k)
{
    if (l==r)
    {
        t[o]&=k;
        s[o]&=k;
        sf[o]=s[o]*s[o]%mo;
        return;
    }
    if (l>=x&&r<=y)
    {
        if (ck(t[o],k)) return;
    }
    if (x<=mid) update(o<<1,l,mid,x,y,k);
    if (y>mid) update(o<<1|1,mid+1,r,x,y,k);
    pushup(o);
}
ll query(int o,int l,int r,int x,int y)
{
    if (l>=x&&r<=y) return s[o];
    ll re=0;
    if (x<=mid) re=re+query(o<<1,l,mid,x,y);
    if (y>mid) re=re+query(o<<1|1,mid+1,r,x,y);
    return re; 
}
ll que(int o,int l,int r,int x,int y)
{
    if (l>=x&&r<=y) return sf[o];
    ll re=0;
    if (x<=mid) re=(re+que(o<<1,l,mid,x,y));
    if (y>mid) re=(re+que(o<<1|1,mid+1,r,x,y));
    return re;
}
int main()
{
    freopen("seg.in","r",stdin);
    freopen("seg.out","w",stdout);
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    int q=read();
    int op,l,r,k;
    while (q--)
    {
        op=read();l=read();r=read();
        if (op==1)
        {
            k=read();
            update(1,1,n,l,r,k);
        }
        if (op==2)
        {
            printf("%lld\n",query(1,1,n,l,r));
        }
        if (op==3)
        {
            ll S1=query(1,1,n,l,r)%mo;
            ll S2=que(1,1,n,l,r);
            ll ans=(2*S2%mo*(r-l+1)%mo+2*S1%mo*S1%mo)%mo;
            printf("%lld\n",ans);
        }
    }
    return 0;
}
View Code

T3:信标 

题目链接:

https://jzoj.net/senior/#contest/show/2545/2

题目:

题解:

以下来自题解

 设一个节点有 c 个儿子, 发现必须在其中至少 c − 1 个儿子的子树中放置信标.

证明如下: 考虑如果不这样放, 对于两棵都没有放的子树, 他们汇集到 lca 上以后距离都是相等的, 所以 lca 外的信标无法区分, 而内部没有信标. 所以不能存在两颗子树都不放. 所以至少要放 c − 1 个. 由于在根节点放置了信标, 可以只考虑深度相同的点. 由于深度相同, 所以他们的 lca 度数至少 为 2, 那么一定有一个信标在 lca 包含这两个点的两支子树中. 那么另一侧的点肯定要走更远的路, 会被区分开. 所以放 c − 1 个足够区分.

这样问题变成每个节点要有 c − 1 棵子树放有信标, 求最小方案. 直接贪心即可. 由于枚举根所 以复杂度为 $O(n^2 )$, 可以获得 70 分.

如何做到$ O(n)$? 我们先特判链的情况答案为 1, 然后找到任意一个度数大于 2 的节点, 可以证 明这个点一定不需要放置信标. 于是以这个点作根$ O(n) $的贪心即可.

证明如下: 深度相同的点对证明同上, 只考虑深度不同的点对.

如果它们在一颗子树中, 由于度数大于 2 所 以一定有另一颗子树的一个信标把他们区分开.

如果在不同的子树中, 有两种情况:

一个在没放信标的子树中, 一个在放了的子树中. 显然还存在另一个子树放了信标, 由于深度不 同他们会被这个信标区分开.

两个都在放了信标的子树中. 如果根的度数大于 3 则同上. 度数等于 3 时, 如果他们没有被区分 开, 一定是他们先汇集到了一个节点上, 然后走到同一个信标上. 这个点一定是一条奇链的中点, 且 不是根 (由于深度不同), 是在两个子树之一中唯一的. 那么他们走到另一个信标就一定有一个点走 了冤枉路, 既另一个信标可以区分出他们.

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;

const int N=1e6+15;
const int inf=1e9;
int n,tot;
int head[N],f[N],deg[N];
struct EDGE{
    int to,nxt;
}edge[N<<1];
inline int read(){
    char ch=getchar();int s=0,f=1;
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
void link(int u,int v){edge[++tot]=(EDGE){v,head[u]};head[u]=tot;}
void dfs(int x,int pre)
{
    int cnt=0,s=0,p0=0;
    for (int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if (y==pre) continue;
        dfs(y,x);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        s+=f[y];
        if (f[y]==0) p0++;
        ++cnt;
    }
    if (cnt==0) {f[x]=0;return;}
    f[x]=s;
    if (p0>=1) f[x]+=p0-1;
}
int main()
{
    freopen("beacon.in","r",stdin);
    freopen("beacon.out","w",stdout);
    n=read();
    if (n==1) {puts("0");return 0;}
    for (int i=1,u,v;i<n;i++){
        u=read();v=read();
        link(u,v);
        link(v,u);
        deg[u]++;deg[v]++;
    }
    int s=0;
    for (int i=1;i<=n;i++) if (deg[i]>=3) s=i;
    if (!s) {puts("1");return 0;}
    dfs(s,-1);
    printf("%d\n",f[s]);
    return 0;
}
View Code
posted @ 2018-11-01 19:51  星星之火OIer  阅读(195)  评论(0编辑  收藏  举报