2017-10-5 清北刷题冲刺班a.m

Posted Soda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017-10-5 清北刷题冲刺班a.m相关的知识,希望对你有一定的参考价值。

行列式

 

序列

#include<iostream>
#include<cstdio>
#define maxn 500010
using namespace std;
int n,m,mod,l,r,x,y,b[maxn],a[maxn],cnt;
void dfs(int now[],int sz){
    if(sz<=2){
        for(int i=1;i<=sz;i++)b[++cnt]=now[i];
        return;
    }
    int sz1=0,sz2=0;
    int d[sz],c[sz];
    for(int i=1;i<=sz;i++){
        if(i%2!=0){//奇数位 
            c[++sz1]=now[i]; 
        }
        if(i%2==0){//偶数位 
            d[++sz2]=now[i];
        }
    }
    dfs(c,sz1);
    dfs(d,sz2);
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("seq.in","r",stdin);freopen("seq.out","w",stdout);
    scanf("%d%d%d",&n,&m,&mod);
    for(int i=1;i<=n;i++)a[i]=i;
    dfs(a,n);
    long long ans;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&l,&r,&x,&y);
        ans=0;
        if(x>y)swap(x,y);
        for(int j=l;j<=r;j++){
            if(b[j]<=y&&b[j]>=x){
                ans=(ans+b[j])%mod;
            }
        }printf("%I64d\\n",ans);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}
30分 暴力
/*
    考虑类似线段树的求解方法,记 getans(n,l,r,x,y) 表示当前在 F 中,是 1 到 n 的升序
    排列,需要求得最终排好序后 l 到 r 范围内,大小在 x 到 y 之间的数值之和以及数字个数
    (getans 返回一个 pair) ,思考如何分治。
    注意到左右分裂的规律,可以算出此时序列需要向左边和右边分出多少,同时可以知道
    l,r,x,y 四个数在子区间的大小,分治下去求解。在回溯时,将左右子树答案合并即可。
    注意如果实现过程中会有类平方运算,可能会超 Long Long 范围,需要特别注意处理。
    具体实现详见代码,复杂度为 O(M logN)。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#ifdef WIN32
#define LL "%I64d"
#else 
#define LL "%lld"
#endif
using namespace std;
struct node{
    long long a,b;
};
long long n,m,mod,l,r,u,v;
node make_node(long long x,long long y){
    node w;
    w.a=x;w.b=y;
    return w;
}
node solve(long long rr,long long l,long long r,long long x,long long y){
    if(x>rr||l>r)return make_node(0,0);
    if(l==1&&r==rr){
        x=max(x,1LL);
        y=min(y,rr);
        long long s;
        if((x+y)%2==0)s=((x+y)/2)%mod*((y-x+1)%mod)%mod;
        else s=((x+y)%mod)*((y-x+1)/2%mod)%mod;
        return make_node(s%mod,y-x+1);
    }
    long long mid=(rr+1)/2;
    if(r<=mid){
        node res=solve(mid,l,r,x/2+1,(y+1)/2);
        return make_node((res.a*2-res.b)%mod,res.b);
    }
    else if(l>mid){
        node res=solve(rr-mid,l-mid,r-mid,(x+1)/2,y/2);
        return make_node(res.a*2%mod,res.b);
    }
    else{
        node res1=solve(mid,l,mid,x/2+1,(y+1)/2);
        node res2=solve(rr-mid,1,r-mid,(x+1)/2,y/2);
        return make_node((res1.a*2-res1.b+res2.a*2)%mod,(res1.b+res2.b)%mod);
    }
}
int main(){
    freopen("seq.in","r",stdin);freopen("seq.out","w",stdout);
    //freopen("Cola.txt","r",stdin);
    scanf(LL LL LL,&n,&m,&mod);
    for(int i=0;i<m;i++){
        scanf(LL LL LL LL,&l,&r,&u,&v);
        node ans=solve(n,l,r,u,v);
        printf(LL"\\n",(ans.a+mod)%mod);
    }
    return 0;
}
100分

 

数数(倍增)

 

 

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define maxn 100010
using namespace std;
int num,head[maxn];
int sz[maxn],son[maxn],fa[maxn],top[maxn],dep[maxn];
long long ans;
struct node{
    int to,pre;
}e[maxn*2];
int n,dis[2010][2010];
bool vis[maxn];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
void Bfs(int s){
    queue<int>q;
    memset(vis,0,sizeof(vis));
    vis[s]=1;q.push(s);dis[s][s]=0;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(vis[to])continue;
            else {
                dis[s][to]=dis[s][now]+1;
                vis[to]=1;
                q.push(to);
            }
        }
    }
}
int count(int x){
    int res=0;
    while(x){
        res+=x&1;
        x>>=1;
    }
    return res;
}
void dfs1(int now,int father){
    dep[now]=dep[father]+1;
    sz[now]=1;fa[now]=father;
    for(int i=head[now];i;i=e[i].pre){
        int to=e[i].to;
        if(to==father)continue;
        dfs1(to,now);
        sz[now]+=sz[to];
        if(!son[now]||sz[son[now]]<sz[to])son[now]=to;
    }
}
void dfs2(int now,int father){
    top[now]=father;
    if(son[now])dfs2(son[now],father);
    for(int i=head[now];i;i=e[i].pre){
        int to=e[i].to;
        if(to==fa[now]||to==son[now])continue;
        dfs2(to,to);
    }
}
int LCA(int a,int b){
    while(top[a]!=top[b]){
        if(dep[top[a]]<dep[top[b]])swap(a,b);
        a=fa[top[a]];
    }
    if(dep[a]>dep[b])swap(a,b);
    return a;
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("bitcount.in","r",stdin);freopen("bitcount.out","w",stdout);
    scanf("%d",&n);
    int x,y;
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        Insert(x,y);Insert(y,x);
    }
    for(int i=1;i<=n;i++)Bfs(i);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dis[i][j]=count(dis[i][j]);
    dfs1(1,0);
    dfs2(1,1);
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int lca=LCA(i,j);
            ans+=dis[i][lca]+dis[j][lca];
        }
    }
    cout<<ans;
    fclose(stdin);fclose(stdout);
    return 0;
}
60分 暴力
/*
    记 l(i,j) 表示 i 号节点向上 2^j 步的祖先节点。
    记 f(i,j) 表示到 i 号节点,上一步长为 2^j 的数位之和。
    记 g(i,j) 表示到 i 号节点,上一步长为 2^j 的方案数。
    考虑转移方程:
    g(i,j) → g(l(i,k),k),k < j
    f(i,j) + g(i,j) → f(l(i,k),k),k < j
    由于难以查询某一节点子树中深度为 d 的节点,该方程用自下而上更新的方法比较方便。
    而 Ans 也很难用一个简洁的式子表示出,故在模拟向上跳跃的时候同步更新。
    具体细节详见代码,复杂度 O(N log^2 N)。
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>

#define st first
#define nd second
using namespace std;

struct edge {
    int x;
    int nxt;
};
typedef long long LL;

const int N = 1E5 + 10;
edge e[2 * N];
int lca[N][17], hd[N], fa[N], sons[N], nxt[N], cnt[N][17], f[N][17];
int n, m, x, y, l;
LL ans;

void link(int x, int y) {
    e[++l].x = y;
    e[l].nxt = hd[x];
    hd[x] = l;
}

void dfs_lca(int x) {
    lca[x][0] = fa[x];
    sons[x] = 1;
    for (int i = 1; i <= 16; ++i)
        lca[x][i] = lca[lca[x][i - 1]][i - 1];
    for (int p = hd[x]; p; p = e[p].nxt)
        if (e[p].x != fa[x]) {
            fa[e[p].x] = x;
            dfs_lca(e[p].x);
            sons[x] += sons[e[p].x];
        }
}

void dfs_ans(int x) {
    for (int p = hd[x]; p; p = e[p].nxt)
        if (e[p].x != fa[x]) nxt[x] = e[p].x, dfs_ans(e[p].x);
    for (int i = 0; i <= 16; ++i) {
        ans += sons[lca[x][i]] - sons[nxt[lca[x][i]]];
        cnt[lca[x][i]][i]++;
        f[lca[x][i]][i]++;
    }
    for (int i = 1; i <= 16; ++i)
        for (int j = 0; j <= i - 1; ++j) {
            ans += LL(cnt[x][i] + f[x][i]) * LL(sons[lca[x][j]] - sons[nxt[lca[x][j]]]);
            cnt[lca[x][j]][j] += cnt[x][i];
            f[lca[x][j]][j] += f[x][i] + cnt[x][i];
        }
}

int main() {
    freopen("bitcount.in", "r", stdin);
    freopen("bitcount.out", "w", stdout);
    scanf("%d", &n);
    for (int i = 1; i < n; ++i) {
        scanf("%d%d", &x, &y);
        link(x, y);
        link(y, x);
    }
    dfs_lca(1);
    sons[0] = sons[1];
    nxt[0] = 1;
    dfs_ans(1);
    printf("%I64d\\n", ans);
}
100分 倍增

 

以上是关于2017-10-5 清北刷题冲刺班a.m的主要内容,如果未能解决你的问题,请参考以下文章

2017-10-3 清北刷题冲刺班a.m

2017-10-2 清北刷题冲刺班a.m

2017-10-1 清北刷题冲刺班a.m

2017-10-4 清北刷题冲刺班a.m

2017-10-7 清北刷题冲刺班a.m

2017-10-5 清北刷题冲刺班p.m