noi.ac上的一套(假)NOI题
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题的更多相关文章
- # NOI.AC省选赛 第五场T1 子集,与&最大值
NOI.AC省选赛 第五场T1 A. Mas的童年 题目链接 http://noi.ac/problem/309 思路 0x00 \(n^2\)的暴力挺简单的. ans=max(ans,xor[j-1 ...
- NOI.AC NOIP模拟赛 第六场 游记
NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...
- NOI.AC NOIP模拟赛 第三场 补记
NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...
- NOI.AC 32 Sort——分治
题目:http://noi.ac/problem/32 从全是0和1的情况入手,可以像线段树一样分治下去,回到本层的时候就是左半部的右边是1,右半部的左边是0,把这两部分换一下就行.代价和时间一样是n ...
- NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)
题目:http://noi.ac/problem/31 模拟 kruscal 的建最小生成树的过程,我们应该把树边一条一条加进去:在加下一条之前先把权值在这一条到下一条的之间的那些边都连上.连的时候要 ...
- NOI.AC #31 MST —— Kruskal+点集DP
题目:http://noi.ac/problem/31 好题啊! 题意很明白,对于有关最小生成树(MST)的题,一般是要模拟 Kruskal 过程了: 模拟 Kruskal,也就是把给出的 n-1 条 ...
- [NOI.AC 2018NOIP模拟赛 第三场 ] 染色 解题报告 (DP)
题目链接:http://noi.ac/contest/12/problem/37 题目: 小W收到了一张纸带,纸带上有 n个位置.现在他想把这个纸带染色,他一共有 m 种颜色,每个位置都可以染任意颜色 ...
- NOI.AC#2139-选择【斜率优化dp,树状数组】
正题 题目链接:http://noi.ac/problem/2139 题目大意 给出\(n\)个数字的序列\(a_i\).然后选出一个不降子序列最大化子序列的\(a_i\)和减去没有任何一个数被选中的 ...
- NOI.AC#2144-子串【SAM,倍增】
正题 题目链接:http://noi.ac/problem/2144 题目大意 给出一个字符串\(s\)和一个序列\(a\).将字符串\(s\)的所有本质不同子串降序排序后,求有多少个区间\([l,r ...
随机推荐
- http之请求报文request
https://blog.csdn.net/blueheart20/article/details/45174399 户端发送一个HTTP请求到服务器的请求消息包括以下格式: 请求行(request ...
- 19重定向管道与popen模型
重定向 dup2 int dup(int fd) 重定向文件描述符 int newFd = dup(STDOUT_FILENO) newFd 指向 stdout int dup2(int fd1, ...
- 大喜python版opencv3发布,demo脚本抢鲜版发布
大喜,python版opencv3发布 zwPython3的升级也可以启动了,一直在等这个,zwPython会直接升级到版本3:zwPython3 zwPython3采用64位python3,支持op ...
- Java实现动态规划法求解0/1背包问题
摘要: 使用动态规划法求解0/1背包问题. 难度: 初级 0/1背包问题的动态规划法求解,前人之述备矣,这里所做的工作,不过是自己根据理解实现了一遍,主要目的还是锻炼思维和编程能力,同时,也是为了增进 ...
- Html常用标记总结
超文本标记语言的结构包括“头”部分(英语:Head).和“主体”部分(英语:body),其中“头”部提供关于网页的信息,“主体”部分提供网页的具体内容. Web页面绝大多数都是由html所编写的. 一 ...
- 【软件位置】Linux查看软件安装的位置
如果我们在Linux 系统上安装了某个软件,我们可以通过如下的三种方式来确定. 一. Which 命令 Shell 的which 命令可以找出相关命令是否已经在搜索路径中. 如: [ro ...
- CF_400_D
codeforces_400_D 题目大意:给出n扇门,m把钥匙,和没把钥匙可以改变状态(关->开,开->关>)的门的数量及对应编号(保证每个门被两把钥匙控制),现给出n扇门的初始状 ...
- Flask 2 程序的基本结构1
NOTE 1.初始化:所有的Flask程序都必须创建一个程序实例. app = Flask(__name__) # 向Flask的构造函数传入参数__name__ 2.路由和视图函数(VF): Cli ...
- DDMS
DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务
- Tornado源码分析 --- Cookie和XSRF机制
Cookie和Session的理解: 具体Cookie的介绍,可以参考:HTTP Cookie详解 可以先查看之前的一篇文章:Tornado的Cookie过期问题 XSRF跨域请求伪造(Cross-S ...