noi.ac上的一套(假)NOI题

本来想着可以刷点通过量的,结果发现好像并不是这样的。

整数

description

给你\(n,p\),要你求\(\sum_{k=1}^n\sum_{i=1}^k\sum_{j=1}^k\gcd(i,j,k) \mod p\)。

\(n\le3\times10^8\)

sol

\[\sum_{k=1}^n\sum_{i=1}^k\sum_{j=1}^k\gcd(i,j,k)=\sum_{d=1}^nd\sum_{k=1}^n\sum_{i=1}^k\sum_{j=1}^k[\gcd(i,j,k)=d]\\=\sum_{d=1}^nd\sum_{k=1}^{n/d}\sum_{i=1}^k\sum_{j=1}^k[\gcd(i,j,k)=1]\\=\sum_{d=1}^nd\sum_{i=1}^n\mu(i)\sum_{j=1}^{n/id}j^2\\=\sum_{d=1}^nd\sum_{i=1}^n\mu(i)\frac{\lfloor\frac n{id}\rfloor(\lfloor\frac n{id}\rfloor+1)(2\lfloor\frac n{id}\rfloor+1)}{6}\\=\sum_{T=1}^n\frac{\lfloor\frac nT\rfloor(\lfloor\frac nT\rfloor+1)(2\lfloor\frac nT\rfloor+1)}{6}\sum_{d|T}d\mu(\frac Td)\\=\sum_{T=1}^n\frac{\lfloor\frac nT\rfloor(\lfloor\frac nT\rfloor+1)(2\lfloor\frac nT\rfloor+1)}{6}\varphi(T)\]

前半部分数论分块,后半部分用杜教求\(\varphi(n)\)的前缀和。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e6+5;
int n,mod,inv,zhi[N],pri[N],tot,phi[N],Phi[N],ans;
int fastpow(int a,int b){
    int res=1;
    while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    return res;
}
int S(int x){
    return 1ll*x*(x+1)%mod*(x+x+1)%mod*inv%mod;
}
int P(int x){
    if (x<N) return phi[x];
    if (~Phi[n/x]) return Phi[n/x];
    int res=0;
    for (int i=2,j;i<=x;i=j+1){
        j=x/(x/i);
        res=(res+1ll*(j-i+1)*P(x/i))%mod;
    }
    res=((1ll*x*(x+1)>>1)-res+mod)%mod;
    return Phi[n/x]=res;
}
int main(){
    scanf("%d%d",&n,&mod);inv=fastpow(6,mod-2);
    phi[1]=1;
    for (int i=2;i<N;++i){
        if (!zhi[i]) pri[++tot]=i,phi[i]=i-1;
        for (int j=1;i*pri[j]<N;++j){
            zhi[i*pri[j]]=1;
            if (i%pri[j]) phi[i*pri[j]]=phi[i]*(pri[j]-1);
            else {phi[i*pri[j]]=phi[i]*pri[j];break;}
        }
    }
    for (int i=1;i<N;++i) phi[i]=(phi[i]+phi[i-1])%mod;
    memset(Phi,-1,sizeof(Phi));
    for (int i=1,j;i<=n;i=j+1){
        j=n/(n/i);
        ans=(ans+1ll*(P(j)-P(i-1)+mod)*S(n/i))%mod;
    }
    printf("%d\n",ans);return 0;
}

蚯蚓排队

description

有一棵\(n\)个点的树和\(m\)只蚯蚓,你需要给条蚯蚓在树上找一个节点住下来,存在\(q\)条限制,每条形如蚯蚓\(a\)跟蚯蚓\(b\)在树上居住的节点间的简单路径经过点\(c\)。求一组合法解。

\(n,m\le250,q\le5\times10^4\)

sol

\(\mbox{2-sat}\)。是HiddenRabbits这个题的弱化版。

建立\(2nm\)个变量表示第\(i\)只蚯蚓在/不在\(j\)的子树中。需要预处理一些连边。对于每条限制,可以O(度数)​地连边。总边数大概是\(O(mn^2+qn)\)级别的。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 2e5+5;
const int M = 3e7+5;
int n,m,q,TO[N],NXT[N],HD[N],tot,fa[N],dep[N],st[N],ed[N];
int S[255][255],to[M],nxt[M],head[N],cnt,dfn[N],low[N],tim,vis[N],s[N],bel[N],scc;
void dfs(int u,int f){
    fa[u]=f;dep[u]=dep[f]+1;st[u]=++tim;
    for (int e=HD[u];e;e=NXT[e])
        if (TO[e]!=f) dfs(TO[e],u);
    ed[u]=tim;
}
bool in(int u,int v){return st[u]>=st[v]&&st[u]<=ed[v];}
void link(int u,int v){
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
    u^=1;v^=1;swap(u,v);
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void Tarjan(int u){
    dfn[u]=low[u]=++tim;vis[s[++s[0]]=u]=1;
    for (int e=head[u];e;e=nxt[e])
        if (!dfn[to[e]]) Tarjan(to[e]),low[u]=min(low[u],low[to[e]]);
        else if (vis[to[e]]) low[u]=min(low[u],dfn[to[e]]);
    if (dfn[u]==low[u]){
        ++scc;int x;
        do x=s[s[0]--],bel[x]=scc,vis[x]=0;while (x^u);
    }
}
int main(){
    n=gi();m=gi();q=gi();
    for (int i=1;i<n;++i){
        int u=gi(),v=gi();
        TO[++tot]=v;NXT[tot]=HD[u];HD[u]=tot;
        TO[++tot]=u;NXT[tot]=HD[v];HD[v]=tot;
    }
    dfs(1,0);tot=-1;
    for (int i=1;i<=m;++i)
        for (int j=1;j<=n;++j)
            S[i][j]=++tot,++tot;
    for (int i=1;i<=m;++i){
        link(S[i][1]^1,S[i][1]);
        for (int j=2;j<=n;++j)
            link(S[i][j],S[i][fa[j]]);
        for (int j=2;j<=n;++j)
            for (int k=j+1;k<=n;++k)
                if (!in(j,k)&&!in(k,j)) link(S[i][j],S[i][k]^1);
    }
    while (q--){
        int a=gi(),b=gi(),c=gi();
        for (int e=HD[c];e;e=NXT[e])
            if (TO[e]!=fa[c])
                link(S[a][TO[e]],S[b][TO[e]]^1);
        if (c>1) link(S[a][c]^1,S[b][c]);
    }
    for (int i=tim=0;i<=tot;++i) if (!dfn[i]) Tarjan(i);
    for (int i=1;i<=m;++i){
        int res=0;
        for (int j=1;j<=n;++j)
            if (bel[S[i][j]]<bel[S[i][j]^1]) res=dep[j]>dep[res]?j:res;
        printf("%d ",res);
    }
    return puts(""),0;
}

泳池

description

有一个\(n\times m\)的矩形,每个地方可以填一个非负整数。要求使得第\(i\)行的最大值为\(a_i\),第\(i\)列的最大值为\(b_i\)。求方案数模\(10^9+9\)。

\(n,m\le200\)

sol

可以先考虑一个子问题:有一个\(n\times m\)的矩形,每个地方可以填\([0,h]\)中的一个数,要求每行每列的最大值都恰好是\(h\)。求方案数。

考虑容斥。枚举\(i\)行\(j\)列强制没有达到最大值,则有

\[Ans=\sum_{i=0}^n\sum_{j=0}^m(-1)^{i+j}\binom ni\binom mjh^{ij}(h+1)^{nm-ij}\]

回到原题。把最大值相同的那些行与列放在一起考虑,可以得到一个类似上式的结论。

code

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 205;
const int mod = 1e9+9;
int n,m,C[N][N],a[N],b[N],Ans=1;
int fastpow(int a,int b){
    int res=1;
    while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    return res;
}
bool cmp(int i,int j){return i>j;}
int main(){
    n=gi();m=gi();
    for (int i=C[0][0]=1;i<N;++i)
        for (int j=C[i][0]=1;j<=i;++j)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    for (int i=1;i<=n;++i) a[i]=gi();sort(a+1,a+n+1,cmp);
    for (int i=1;i<=m;++i) b[i]=gi();sort(b+1,b+m+1,cmp);
    a[n+1]=b[m+1]=-1;
    for (int x=0,y=0;x<n||y<m;){
        int h=max(a[x+1],b[y+1]),xx=x,yy=y,ans=0;
        while (a[x+1]==h) ++x;while (b[y+1]==h) ++y;
        for (int i=0;i<=x-xx;++i)
            for (int j=0;j<=y-yy;++j){
                int S1=(x-i)*(y-j)-xx*yy,S2=x*y-S1-xx*yy;
                int res=1ll*C[x-xx][i]*C[y-yy][j]%mod*fastpow(h+1,S1)%mod*fastpow(h,S2)%mod;
                if ((i+j)&1) ans=(ans-res+mod)%mod;
                else ans=(ans+res)%mod;
            }
        Ans=1ll*Ans*ans%mod;
    }
    printf("%d\n",Ans);return 0;
}

游戏

description

给你一个数列\(\{a_i\}\),每次选出两个不相交的区间\([l_1,r_1],[l_2,r_2]\),要求\(r_1<l_2\),此时获得的收益是两个区间的区间最小值的最小值。求所有不同的选法的收益之和模\(10^9+7\) 。

\(n\le2\times10^5\)

sol

可以视作不存在原序列中相同的值,若存在则按下标为第二关键字排序即可。

考虑枚举一个位置\(p\),计算其作为最小值的方案数。

找到\(p\)左右两边第一个小于\(p\)的位置\(l,r\),那么两个区间的选取就有三种方式:

1、\(l_1\in[l,p],r_1\in[p,r],l_2,r_2\in[p+1,r]\);

2、\(r_2\in[p,r],l_2\in[l,p],l_1,r_1\in[l,p-1]\);

3、一个区间的左端点\(\in[l,p]\)右端点\(\in[p,r]\),另一个区间不包含于\([l,r]\),且最小值不大于\(a_p\)。

前两种好算,最后一种在计算时需要维护“另一个区间”的个数。按照权值从小到大处理即可。

复杂度\(O(n\log n)\)。

code

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 2e5+5;
const int mod = 1e9+7;
int n,a[N],b[N],S[N],top,L[N],R[N],sum[N],num,ans;
bool cmp(int i,int j){return a[i]==a[j]?i<j:a[i]<a[j];}
int main(){
    n=gi();num=(1ll*n*(n+1)>>1)%mod;
    for (int i=1;i<=n;++i) a[i]=gi(),b[i]=i;
    sort(b+1,b+n+1,cmp);
    for (int i=1;i<=n;++i){
        while (top&&a[S[top]]>a[i]) --top;
        L[i]=S[top]+1;S[++top]=i;
    }
    S[top=0]=n+1;
    for (int i=n;i;--i){
        while (top&&a[S[top]]>=a[i]) --top;
        R[i]=S[top]-1;S[++top]=i;
    }
    for (int i=1;i<=n;++i) sum[i]=(sum[i-1]+(1ll*i*(i+1)>>1))%mod;
    for (int i=1;i<=n;++i){
        int p=b[i],l=L[p],r=R[p],res=(1ll*(r-l+1)*(r-l+2)>>1)%mod;
        res=1ll*(p-l+1)*(r-p+1)%mod*(num-res+mod)%mod;
        res=(res+1ll*(p-l+1)*sum[r-p])%mod;
        res=(res+1ll*(r-p+1)*sum[p-l])%mod;
        ans=(ans+1ll*res*a[p])%mod;
        num=(num-1ll*(p-l+1)*(r-p+1)%mod+mod)%mod;
    }
    printf("%d\n",ans);return 0;
}

咕咕咕?

noi.ac上的一套(假)NOI题的更多相关文章

  1. # NOI.AC省选赛 第五场T1 子集,与&最大值

    NOI.AC省选赛 第五场T1 A. Mas的童年 题目链接 http://noi.ac/problem/309 思路 0x00 \(n^2\)的暴力挺简单的. ans=max(ans,xor[j-1 ...

  2. NOI.AC NOIP模拟赛 第六场 游记

    NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...

  3. NOI.AC NOIP模拟赛 第三场 补记

    NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...

  4. NOI.AC 32 Sort——分治

    题目:http://noi.ac/problem/32 从全是0和1的情况入手,可以像线段树一样分治下去,回到本层的时候就是左半部的右边是1,右半部的左边是0,把这两部分换一下就行.代价和时间一样是n ...

  5. NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)

    题目:http://noi.ac/problem/31 模拟 kruscal 的建最小生成树的过程,我们应该把树边一条一条加进去:在加下一条之前先把权值在这一条到下一条的之间的那些边都连上.连的时候要 ...

  6. NOI.AC #31 MST —— Kruskal+点集DP

    题目:http://noi.ac/problem/31 好题啊! 题意很明白,对于有关最小生成树(MST)的题,一般是要模拟 Kruskal 过程了: 模拟 Kruskal,也就是把给出的 n-1 条 ...

  7. [NOI.AC 2018NOIP模拟赛 第三场 ] 染色 解题报告 (DP)

    题目链接:http://noi.ac/contest/12/problem/37 题目: 小W收到了一张纸带,纸带上有 n个位置.现在他想把这个纸带染色,他一共有 m 种颜色,每个位置都可以染任意颜色 ...

  8. NOI.AC#2139-选择【斜率优化dp,树状数组】

    正题 题目链接:http://noi.ac/problem/2139 题目大意 给出\(n\)个数字的序列\(a_i\).然后选出一个不降子序列最大化子序列的\(a_i\)和减去没有任何一个数被选中的 ...

  9. NOI.AC#2144-子串【SAM,倍增】

    正题 题目链接:http://noi.ac/problem/2144 题目大意 给出一个字符串\(s\)和一个序列\(a\).将字符串\(s\)的所有本质不同子串降序排序后,求有多少个区间\([l,r ...

随机推荐

  1. http之请求报文request

    https://blog.csdn.net/blueheart20/article/details/45174399 户端发送一个HTTP请求到服务器的请求消息包括以下格式: 请求行(request ...

  2. 19重定向管道与popen模型

    重定向 dup2 int dup(int fd) 重定向文件描述符  int newFd = dup(STDOUT_FILENO) newFd 指向 stdout int dup2(int fd1, ...

  3. 大喜python版opencv3发布,demo脚本抢鲜版发布

    大喜,python版opencv3发布 zwPython3的升级也可以启动了,一直在等这个,zwPython会直接升级到版本3:zwPython3 zwPython3采用64位python3,支持op ...

  4. Java实现动态规划法求解0/1背包问题

    摘要: 使用动态规划法求解0/1背包问题. 难度: 初级 0/1背包问题的动态规划法求解,前人之述备矣,这里所做的工作,不过是自己根据理解实现了一遍,主要目的还是锻炼思维和编程能力,同时,也是为了增进 ...

  5. Html常用标记总结

    超文本标记语言的结构包括“头”部分(英语:Head).和“主体”部分(英语:body),其中“头”部提供关于网页的信息,“主体”部分提供网页的具体内容. Web页面绝大多数都是由html所编写的. 一 ...

  6. 【软件位置】Linux查看软件安装的位置

    如果我们在Linux 系统上安装了某个软件,我们可以通过如下的三种方式来确定. 一.        Which 命令 Shell 的which 命令可以找出相关命令是否已经在搜索路径中. 如: [ro ...

  7. CF_400_D

    codeforces_400_D 题目大意:给出n扇门,m把钥匙,和没把钥匙可以改变状态(关->开,开->关>)的门的数量及对应编号(保证每个门被两把钥匙控制),现给出n扇门的初始状 ...

  8. Flask 2 程序的基本结构1

    NOTE 1.初始化:所有的Flask程序都必须创建一个程序实例. app = Flask(__name__) # 向Flask的构造函数传入参数__name__ 2.路由和视图函数(VF): Cli ...

  9. DDMS

    DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务

  10. Tornado源码分析 --- Cookie和XSRF机制

    Cookie和Session的理解: 具体Cookie的介绍,可以参考:HTTP Cookie详解 可以先查看之前的一篇文章:Tornado的Cookie过期问题 XSRF跨域请求伪造(Cross-S ...