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

来源:互联网 发布:nginx 查看模块 编辑:程序博客网 时间:2024/04/23 16:29

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 secondusing 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 secondusing 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);}