国庆清北刷题冲刺班 Day5 上午

Day4 下午的跳了 哈哈哈哈

行列式

文件名:det.cpp(pas)
时间限制:1s
空间限制:512MB
题目描述:
有一个 N ×N 的矩阵 X,且矩阵内元素 X i,j 均为整数,现给出该矩阵第一行的元素,即
X 1,k ,k ∈ [1,N]。
试判断该矩阵的行列式是否有可能为 D。
输入格式:
第一行一个正整数 T,表示本题有 T 组数据。
每组数据第一行为一个正整数 N 和一个整数 D。接下来一行 N 个整数顺序给出 X 1,k 。
输出格式:
对于每组数据,输出一行。Y 表示存在矩阵 X 满足行列式为 D,N 表示不存在。
样例读入:
2
2 7
1 0
3 1
2 2 3
样例输出:
Y
Y
样例解释:
给出样例的一种解释方案:
det
(
1 0
0 7
)
= 7 det



2 2 3
0 −1 0
1 1 1



= 1
数据范围:
对于 30% 的数据,保证 T ⩽ 10,N ⩽ 10,|X 1,k | ⩽ 10,|D| ⩽ 10
对于 60% 的数据,保证 T ⩽ 100,N ⩽ 100,|X 1,k | ⩽ 1000,|D| ⩽ 1000
对于 100% 的数据,保证 T ⩽ 1000,N ⩽ 1000,|X 1,k | ⩽ 10 9 ,|D| ⩽ 10 9

T1 全场跳题,恩,就这样

序列

文件名:seq.cpp(pas)
时间限制:1s
空间限制:512MB
题目描述:
定义一个对序列操作 F:
F(list) =



F(odd) + F(even), If |list| > 1
list, If |list| = 1
,
即,每次将序列按奇偶下标分成两半,然后在回溯的时候拼接起来,例如:F({1,2,3,4}) =
{1,3,2,4},F({1,2,3,4,5,6,7}) = {1,5,3,7,2,6,4}。
记 {a n } 为 1 到 N 的升序排列,{b n } = F({a n })。
有 M 组询问,每次询问 {b n } 中,下标在 l 到 r 内,大小在 x 到 y 内的值之和 Ans。
由于数据可能很大,请将 Ans 对 mod 取模后输出。
输入格式:
第一行三个正整数,依次为 N,M,mod。
接下来 M 行,每行 4 个正整数,依次为 l,r,x,y。
输出格式:
M 行,对应 M 个询问,依次输出 Ans。
样例读入:
4 3 1000
2 4 1 3
1 3 3 4
1 1 100 200
样例输出:
5
3
0
样例解释:
{b n } = {1,3,2,4},所以 b 2 ,b 3 ,b 4 中在 [1,3] 内的项为 b 2 ,b 3 ,和为 5。
其他类似分析。
数据范围:
对于 30% 的数据,保证 N ⩽ 100,M ⩽ 100
对于 60% 的数据,保证 N ⩽ 10 5 ,M ⩽ 10 4
对于 100% 的数据,保证 N ⩽ 10 18 ,M ⩽ 5 × 10 4 ,1 ⩽ l ⩽ r ⩽ N,1 ⩽ x ⩽ y ⩽ 10 18 ,1 <
mod ⩽ 10

这里写图片描述

可持久化线段树可以通过 60% 的数据,但这并没有用到 {a n } 是 1 到 N 的升序排列的
性质。
考虑类似线段树的求解方法,记 getans(n,l,r,x,y) 表示当前在 F 中,是 1 到 n 的升序
排列,需要求得最终排好序后 l 到 r 范围内,大小在 x 到 y 之间的数值之和以及数字个数
(getans 返回一个 pair) ,思考如何分治。
注意到左右分裂的规律,可以算出此时序列需要向左边和右边分出多少,同时可以知道
l,r,x,y 四个数在子区间的大小,分治下去求解。在回溯时,将左右子树答案合并即可。
注意如果实现过程中会有类平方运算,可能会超 Long Long 范围,需要特别注意处理。
具体实现详见代码,复杂度为 O(M logN)。

考场60分代码:

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

inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); }
}
int n,m,Mod,a[105],b[10000005],c[105],cnt,d[105];

void Build(int l,int r,int dep,int lx,int rx){
    if(l==r) { b[++cnt]=rx;return; }
    int Mid=l+r>>1;
    if((r-l+1)%2==1){
        Build(l,Mid,dep+1,lx,rx);
        Build(Mid+1,r,dep+1,lx+pow(2,dep),rx-pow(2,dep));
    }
    else {
        Build(l,Mid,dep+1,lx,rx-pow(2,dep));
        Build(Mid+1,r,dep+1,lx+pow(2,dep),rx);
    }
}

void build(int l,int r,int dep,int lx,int rx){
    if(l==r) { b[++cnt]=rx; return; }
    int Mid=(l+r)>>1;
    if((r-l+1)%2==1){
        build(l,Mid,dep+1,lx,rx);
        build(Mid+1,r,dep+1,lx+pow(2,dep),rx-pow(2,dep));
    }
    else {
        build(l,Mid,dep+1,lx,rx-pow(2,dep));
        build(Mid+1,r,dep+1,lx+pow(2,dep),rx);
    }
}

int main(int argc,char *argv[]){
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    read(n),read(m),read(Mod);
    Build(1,(n+1)>>1,1,1,n-((n+1)%2));
    build((n+1>>1)+1,n,1,2,n-(n%2));
    long long Ans=0;
    for(int l,r,x,y;m;m--){
        Ans=0;
        read(l),read(r),read(x),read(y);
        if(x>y) swap(x,y);
        if(l>r) swap(l,r);
        if(x>n){
            printf("0\n");
            continue;
        }
        for(int j=l;j<=r;++j){
            if(b[j]>=x&&b[j]<=y)Ans+=b[j];
            if(Ans>=Mod) Ans-=Mod;
        }
        printf("%I64d\n",Ans);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

本来我以为的这个做法只能拿到30分,就开了30% 的数组大小,开大数组之后,发现同样的做法可以拿到60分,gg
AC代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define st first 
#define nd second
using namespace std;

typedef long long LL;
typedef pair<LL,LL> pa;
LL n,m,Mod,l,r,u,v;
inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

pa solve(LL rr, LL l, LL r, LL x, LL y){
    if(x > rr || l > r) return make_pair(0, 0);
    if(l == 1 && r == rr){
        x = max(x, 1LL),y = min(y, rr);
        LL 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_pair(s % Mod, y - x + 1);
    }
    LL Mid = rr + 1 >>1;
    if(r <= Mid){
        pa ret = solve(Mid, l, r, x / 2 + 1, (y + 1) / 2);
        return make_pair((ret.st * 2 - ret.nd) % Mod,ret.nd);
    } else if(l > Mid){
        pa ret = solve(rr - Mid, l - Mid, r - Mid, (x + 1) / 2, y / 2);
        return make_pair(ret.st * 2 % Mod, ret.nd);
    } else {
        pa ret1 = solve(Mid,l,Mid,x / 2 + 1,(y + 1) / 2);
        pa ret2 = solve(rr - Mid,1,r - Mid,(x + 1) / 2,y / 2);
        return make_pair( (ret1.st * 2 - ret1.nd + ret2.st * 2) % Mod,
                          (ret1.nd + ret2.nd) % Mod);
    }
}

int main(int argc,char *argv[]){
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    scanf("%I64d%I64d%I64d",&n,&m,&Mod);
    for(int i=0; i<m; ++i){
        scanf("%I64d%I64d%I64d%I64d", &l, &r, &u, &v);
        pa Ans = solve(n, l, r, u, v);
        printf("%I64d\n", (Ans.st + Mod) % Mod );
    }
    fclose(stdin); fclose(stdout); 
    return 0;
}

数数

文件名:bitcount.cpp(pas)
时间限制:1s
空间限制:512MB
题目描述:
给出一棵 N 个节点,以 1 为根的有根树。
定义树上两个节点 x 和 y 的距离函数 d:
d(x,y) = Dist(x,z) + Dist(y,z),
其中 z 为 x 和 y 的最近公共祖先,Dist(a,b) 为 a 到 b 路径上边数 m 的二进制下 1 的个数。
希望你能求出树上每对节点的 d 值之和,即:
Ans =
n

i=1
n

j=i+1
d(i,j).
输入格式:
第一行一个正整数 N。
接下来 N − 1 行,每行两个正整数 A i ,B i ,表示一条树边连接 A i 和 B i 。
输出格式:
一行,一个整数 Ans。
样例读入:
4
1 2
1 3
2 4
样例输出:
8
样例解释:

这里写图片描述

这里写图片描述

#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,nxt; };
typedef long long LL;
const int N = 1e5 + 10;
struct edge e[2 * N];
int fa[N][17], hd[N], fom[N], siz[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) {
    fa[x][0] = fom[x];
    siz[x] = 1;
    for (int i = 1; i <= 16; ++i)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for (int p = hd[x]; p; p = e[p].nxt)
        if (e[p].x != fom[x]) {
            fom[e[p].x] = x;
            DFS_LCA(e[p].x);
            siz[x] += siz[e[p].x];
        }
}

void DFS_Ans(int x) {
    for (int p = hd[x]; p; p = e[p].nxt)
        if (e[p].x != fom[x]) nxt[x] = e[p].x, DFS_Ans(e[p].x);
    for (int i = 0; i <= 16; ++i) {
        ans += siz[fa[x][i]] - siz[nxt[fa[x][i]]];
        cnt[fa[x][i]][i]++;
        f[fa[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(siz[fa[x][j]] - siz[nxt[fa[x][j]]]);
            cnt[fa[x][j]][j] += cnt[x][i];
            f[fa[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);
    siz[0] = siz[1];
    nxt[0] = 1;
    DFS_Ans(1);
    printf("%I64d\n", ans);
    fclose(stdin); fclose(stdout);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值