4.5&4.7联考题解
本来想加个密码的,后来一想全HE就咱们这几个人,外省的dalao愿看也没事儿,就公开算了,省得加密码各种麻烦。
先补这两天的题解吧……如果有空的话我可能会把上次联考的题解补上= =(中午没睡觉,现在困得很,根本没法写题……
Day 1
算算算number
感觉出题人出题的时候zz了吧,费了半天搞出来一个极其麻烦还跑得慢的做法是要闹哪样啊……
算了,写一写$O(nk)$做法的推导过程吧,虽然其实非常好推……
首先定义$S_i$表示到$1~i$位置的前缀和,并且规定$S_0=0$,那么
\begin{align}Ans_i=&\sum_{j=0}^{i-1}\left(S_i-S_j\right)^k\\=&\sum_{j=0}^{i-1}\sum_{x=0}^k C_k^xS_i^x\left(-S_j\right)^{k-x}\left(二项式定理\right)\\=&\sum_{x=0}^k C_k^x S_i^x\sum_{j=0}^{i-1}\left(-S_j\right)^{k-x}\end{align}
然后就很好搞了,首先枚举$x$,然后维护$\left(-S_j\right)^{k-x}$的前缀和即可。组合数是可以直接一边算一边递推的,不需要再预处理了。
直接算是$O(nk\log k)$的,加上一些线性预处理逆元之类的技巧就可以降到$O(nk+n\log p)$了,实测极限数据只需要不到1.3s就可以出解,并且不需要利用数据随机这个性质。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,p=;
int qpow(int,int);
int fac[maxn],fac_inv[maxn],inv[maxn];
int T,n,k,s[maxn],s_k[maxn],s_inv[maxn],t[maxn],a[maxn],ans[maxn];
int main(){
fac[]=fac_inv[]=;
for(int i=;i<=;i++)fac[i]=(long long)fac[i-]*i%p;
fac_inv[]=qpow(fac[],p-);
for(int i=-;i;i--)fac_inv[i]=(long long)fac_inv[i+]*(i+)%p;
for(int i=;i<=;i++)inv[i]=(long long)fac[i-]*fac_inv[i]%p;
scanf("%d",&T);
while(T--){
memset(s,,sizeof(s));
memset(ans,,sizeof(ans));
scanf("%d%d",&n,&k);
for(int i=;i<=n;i++){
scanf("%1d",&s[i]);
s[i]+=s[i-];
s_k[i]=qpow(s[i],k);
s_inv[i]=qpow(s[i],p-);
}
fill(t,t+n+,);
for(int x=,C=;x<=k;x++){
copy(t,t+n+,a);
for(int i=;i<=n;i++){
a[i]=(a[i]+a[i-])%p;
ans[i]=(ans[i]+(long long)C*s_k[i]%p*a[i-]%p)%p;
}
for(int i=;i<=n;i++){
t[i]=(long long)t[i]*((p-s[i])%p)%p;
s_k[i]=(long long)s_k[i]*s_inv[i]%p;
}
C=(long long)C*(k-x)%p*inv[x+]%p;
}
for(int i=;i<=n;i++)printf("%d ",ans[i]);
printf("\n");
}
return ;
}
int qpow(int a,int b){
int ans=;
for(;b;b>>=,a=(long long)a*a%p)if(b&)ans=(long long)ans*a%p;
return ans;
}
/*
2
10 2
3672415495
10 2
9040607879
Answer:
9 117 474 634 1066 1200 2075 3043 6238 8668
81 81 201 201 633 633 1690 3802 6539 11435
*/
所以你们看,这题就是个基本的二项式定理,换句话说就是比较水的高考题……
买买买buy
T(o)B(e)C(ompleted)……
树树树mst
去%了一发gzz的代码,然后发现我连Prim都不会了……我好菜啊= =
直接处理曼哈顿距离不太方便,可以坐标变换一下变成切比雪夫距离。
每次连边选的是已选集合与未选集合之间最大的一条边,边的端点有四种情况:
1.已选的最左点和未选的最右点
2.已选的最右点和未选的最左点
3.已选的最上点和未选的最下点
4.已选的最下点和未选的最上点
用set或者最大-最小堆维护所有没选的点的$x/y$坐标最大/最小值即可,复杂度$O(n\log n)$(再次吐槽一波出题人zz)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int maxn=;
multiset<pair<int,int> >mx,my;
long long ans=;
int n,tx,ty,x[maxn],y[maxn],maxx,maxy,minx,miny;
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d%d",&tx,&ty);
x[i]=tx+ty;
y[i]=tx-ty;
}
maxx=minx=x[];
maxy=miny=y[];
for(int i=;i<=n;i++){
mx.insert(make_pair(x[i],y[i]));
my.insert(make_pair(y[i],x[i]));
}
for(int k=;k<n;k++){
int w1=maxx-mx.begin()->first,w2=mx.rbegin()->first-minx,w3=maxy-my.begin()->first,w4=my.rbegin()->first-miny,w=max(max(w1,w2),max(w3,w4));
if(w==w1){
pair<int,int>t=*mx.begin();
minx=min(minx,t.first);
maxy=max(maxy,t.second);
miny=min(miny,t.second);
mx.erase(mx.begin());
my.erase(my.find(make_pair(t.second,t.first)));
}
else if(w==w2){
pair<int,int>t=*mx.rbegin();
maxx=max(maxx,t.first);
maxy=max(maxy,t.second);
miny=min(miny,t.second);
mx.erase(mx.find(*mx.rbegin()));
my.erase(my.find(make_pair(t.second,t.first)));
}
else if(w==w3){
pair<int,int>t=*my.begin();
miny=min(miny,t.first);
maxx=max(maxx,t.second);
minx=min(minx,t.second);
my.erase(my.begin());
mx.erase(mx.find(make_pair(t.second,t.first)));
}
else if(w==w4){
pair<int,int>t=*my.rbegin();
maxy=max(maxy,t.first);
maxx=max(maxx,t.second);
minx=min(minx,t.second);
my.erase(my.find(*my.rbegin()));
mx.erase(mx.find(make_pair(t.second,t.first)));
}
ans+=w;
}
printf("%lld",ans);
return ;
}
考场上想到了坐标变换和Prim,但是并没有去细想,自信自己LCT打得很熟练,码完30分大暴力之后就开始码LCT+随机化,结果费了将近一个小时,并且随机化效果还不如暴力,本来可能可以用来思考正解的宝贵时间就这么浪费了……
没办法,自己弱不能怪社会……
Day 2
行了行了,暴力都能写错,我是制杖……
A
注意到一次操作对逆序对产生的影响就是把所有下标$\ge x$的数与后面的数所产生的逆序对全部消掉,并且对一个位置操作后再次操作不会产生任何影响,因此可以定义每个数的贡献为它与后面的数产生的逆序对数,那么每次操作就会使答案减掉所有下标$\ge x$且权值$\le a_x$的数的贡献并把这些数的贡献移除。
如果要直接维护的话可以用树套树或者K-D树。当然离线的话要容易的多,只要求出每个数的贡献消失的时刻就行了,显然这个就是每个数前面权值$\ge a_x$的数中最早被操作的那个,鉴于都是后缀查询,树状数组维护即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
void add(int);
int query_sum(int);
void modify(int,int);
int query_min(int);
long long ans[maxn]={};
int n,m,a[maxn],b[maxn],c[maxn]={},w[maxn],x;
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
copy(a+,a+n+,b+);
sort(b+,b+n+);
for(int i=n;i;i--){
a[i]=lower_bound(b+,b+n+,a[i])-b;
w[i]=query_sum(a[i]-);
ans[]+=w[i];
add(a[i]);
}
fill(b+,b+n+,(~0u)>>);
for(int i=;i<=m;i++){
scanf("%d",&x);
b[x]=min(b[x],i);
}
fill(c+,c+n+,(~0u)>>);
for(int i=;i<=n;i++){
if(b[i]<=m)modify(a[i],b[i]);
int t=query_min(a[i]);
if(t<=m)ans[t]-=w[i];
}
printf("%lld\n",ans[]);
for(int i=;i<=m;i++){
ans[i]+=ans[i-];
printf("%lld\n",ans[i]);
}
return ;
}
void add(int x){
while(x<=n){
c[x]++;
x+=x&-x;
}
}
int query_sum(int x){
int ans=;
while(x){
ans+=c[x];
x&=x-;
}
return ans;
}
void modify(int x,int d){
while(x){
c[x]=min(c[x],d);
x&=x-;
}
}
int query_min(int x){
int ans=(~0u)>>;
while(x<=n){
ans=min(ans,c[x]);
x+=x&-x;
}
return ans;
}
考场上把暴力写挂了,本来在排序的时候应该把原来的权值复制一份拿来比较,我却直接拿修改后的权值比较,居然还有40分……(这种**错误都能犯,这人没救了……
B
首先给9个格子编号,定义$f_{i,j}$表示从$i$走$n$步之后到达$j$的方案数,显然这个是可以大力跑$9$遍矩阵快速幂求出来的,不过注意到每次的转移矩阵都一样,因此只跑一遍快速幂就行了,然后用$9$个不同的初始矩阵乘一下就可以得到最终的矩阵了(当然你利用对称性的话可以把矩乘次数再减少一些,不过并不需要卡这点常数……)。
然后就很好搞了,反正总共只有$9$个格子, $O(9!)$大力枚举每个机器人最后走到了哪儿即可,复杂度$O(9!+9^4+9^3\log n)$。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int p=,dx[]={,-,,},dy[]={,,,-},
id[][]={
{,,},
{,,},
{,,}
};
struct Matrix{
int a[][];
Matrix(int k=){
memset(a,,sizeof(a));
if(k)for(int i=;i<;i++)a[i][i]=k;
}
Matrix operator*(const Matrix &b)const{
Matrix c;
for(int i=;i<;i++)for(int j=;j<;j++)if(a[i][j]){
for(int k=;k<;k++)if(b.a[j][k])c.a[i][k]=(c.a[i][k]+(long long)a[i][j]*b.a[j][k]%p)%p;
}
return c;
}
int *operator[](int x){return a[x];}
}A,B;
Matrix qpow(Matrix,long long);
int f[][],a[],ans=;
long long n;//long long!!!
int main(){
scanf("%lld",&n);
for(int i=;i<;i++)for(int j=;j<;j++){
B[id[i][j]][id[i][j]]=;
for(int k=;k<;k++){
int x=i+dx[k],y=j+dy[k];
if(x<||x>=||y<||y>=)continue;
B[id[x][y]][id[i][j]]++;
}
}
B=qpow(B,n);
for(int i=;i<;i++){
A=Matrix();
A[][i]=;
A=A*B;
for(int j=;j<;j++)f[j][i]=A[][j];
}
//for(int i=0;i<9;i++)for(int j=0;j<9;j++)printf("f[%d][%d]=%d\n",i,j,f[i][j]);
for(int i=;i<;i++)a[i]=i;
do{
int tmp=;
for(int i=;i<;i++)tmp=(long long)tmp*f[i][a[i]]%p;
ans=(ans+tmp)%p;
}while(next_permutation(a,a+));
printf("%d",ans);
return ;
}
Matrix qpow(Matrix a,long long b){
Matrix ans();
for(;b;b>>=,a=a*a)if(b&)ans=ans*a;
return ans;
}
C
我说这是杜教筛板子题你信么……
\begin{align}Ans=&\sum_{i=1}^n\sum_{j=1}^n(i,j)^k\\=&\sum_{d=1}^n d^k\sum_{i=1}^n\sum_{j=1}^n[(i,j)=d]\\=&\sum_{d=1}^n d^k\left(2 S_\varphi\left(\left\lfloor\frac nd\right\rfloor\right)-1\right)\\&其中S_\varphi(n)=\sum_{i=1}^n\varphi(i)\end{align}
只要能算出$d^k$的前缀和就可以分块了,鉴于$k$很小,用差分组合数(牛顿插值)或者伯努利数之类的做法随便搞一搞就可以了。
题解写的复杂度有误,杜教筛在预处理前$n^{\frac 2 3}$项的$\varphi(n)$前缀和之后是可以做到总复杂度$O(n^{\frac 2 3})$的,即使外面套了一层分块也是如此(毕竟杜教筛自带分块形式)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int table_size=,maxn=table_size+,p=,inv_2=;
void phi_table(int);
int S(long long);//long long!!!
int s_k(long long);//long long!!!
int qpow(int,int);
bool notp[maxn]={false},vis[maxn]={false};
int prime[maxn]={},phi[maxn]={},S_phi[maxn]={};
int k,s[],r[],inv[];
long long N;//IMPORTANT!!!Remember to use long long!!!
int main(){
scanf("%lld%d",&N,&k);
for(int i=;i<=k+;i++){
r[i]=qpow(i,k);
s[i]=(r[i]+s[i-])%p;
inv[i]=qpow(i,p-);
}
for(int j=;j<=k+;j++)for(int i=k+;i>=j;i--)r[i]=(r[i]-r[i-]+p)%p;
/* for(;;){
scanf("%lld",&N);
printf("%d\n",s_k(N));
} */
phi_table(min((long long)table_size,N));
long long i=,last;
int ans=,sk=,tmp;
while(i<=N){
last=N/(N/i);
tmp=s_k(last);
ans=(ans+(tmp-sk+p)%p*(long long)((*S(N/i)%p-+p)%p)%p)%p;
sk=tmp;
i=last+;
}
printf("%d",ans);
return ;
}
void phi_table(int n){
phi[]=;
for(int i=;i<=n;i++){
if(!notp[i]){
prime[++prime[]]=i;
phi[i]=i-;
}
for(int j=;j<=prime[]&&i*prime[j]<=n;j++){
notp[i*prime[j]]=true;
if(i%prime[j])phi[i*prime[j]]=phi[i]*(prime[j]-);
else{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
for(int i=;i<=n;i++)phi[i]=(phi[i]+phi[i-])%p;
}
int S(long long n){
if(n<=table_size)return phi[n];
if(vis[N/n])return S_phi[N/n];
vis[N/n]=true;
int ans=n%p*((n+)%p)%p*inv_2%p;
long long i=,last;//long long!!!
while(i<=n){
last=n/(n/i);
ans=(ans-(long long)S(n/i)*((last-i+)%p)%p+p)%p;
i=last+;
}
S_phi[N/n]=ans;
return ans;
}
int s_k(long long n){
if(n<=k+)return s[n];
int ans=,C=;
for(int i=;i<=k+;i++){
ans=(ans+(long long)C*r[i+]%p)%p;
C=(long long)C*((n-i-)%p)%p*inv[i+]%p;
}
return ans;
}
int qpow(int a,int b){
int ans=;
for(;b;b>>=,a=(long long)a*a%p)if(b&)ans=(long long)ans*a%p;
return ans;
}
ps:关于$ \sum_{i=1}^n\sum_{j=1}^n[(i,j)=d]=2S_\varphi\left(\left\lfloor\frac n d\right\rfloor\right)+1$的推导:
\begin{align}&\sum_{i=1}^n\sum_{j=1}^n[(i,j)=d]\\=&2\left(\sum_{i=1}^n\sum_{j=1}^i[(i,j)=d]\right)-1\\=&2\left(\sum_{d|i}^n\sum_{d|j,j\le i}\left[\left(\frac i d,\frac j d\right)=1\right]\right)-1\\=&2\left(\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}\sum_{j=1}^i[(i,j)=1]\right)-1\\=&2\left(\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}\varphi(i)\right)-1\\=&2S_\varphi\left(\left\lfloor\frac n d\right\rfloor\right)-1\\\end{align}
(好吧,感觉大多人都应该知道这个结论,真是废话……)
反思:
Day1在T3的LCT+随机化上浪费了好多时间(真是奇怪,为什么我的LCT有时候能不到半个小时写完不用调,有的时候又要调上大半个小时才能写出来……),导致没有时间细想T3的正解,Day2因为太久不写杜教筛和差分组合数生疏了,在推杜教筛求$S_\varphi(n)$以及求$k$次幂之和上浪费了不少时间,结果写完后两题和A的暴力之后已经没有时间去想A的正解了,两天都是把时间浪费在不应该的地方,这种考试策略的错误一定要注意。
这次联考也再次让我认识到了自己思维的薄弱之处,这些天一定要多加锻炼,不要再看到思维题就束手无策。
话说两天都挂成这样还能压线进A队是要闹哪样……
4.5&4.7联考题解的更多相关文章
- HEOI2018(九省联考) 题解集合
转载请注明出处:http://www.cnblogs.com/LadyLex/p/8792894.html 今年的省选题目真是赞啊……Day2的题完全不会做…… 不过终于卡着校线爬着进了B队 终于改完 ...
- 「ZJOI2019」&「十二省联考 2019」题解索引
「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...
- 题解 P6622 [省选联考 2020 A/B 卷] 信号传递
洛谷 P6622 [省选联考 2020 A/B 卷] 信号传递 题解 某次模拟赛的T2,考场上懒得想正解 (其实是不会QAQ), 打了个暴力就骗了\(30pts\) 就火速溜了,参考了一下某位强者的题 ...
- 【简】题解 P5283 [十二省联考2019]异或粽子
传送门:P5283 [十二省联考2019]异或粽子 题目大意: 给一个长度为n的数列,找到异或和为前k大的区间,并求出这些区间的异或和的代数和. QWQ: 考试时想到了前缀异或 想到了对每个数按二进制 ...
- 三校联考 Day3
三校联考 Day3 大水题 题目描述:给出一个圆及圆上的若干个点,问两个点间的最远距离. solution 按极角排序,按顺序枚举,显然距离最远的点是单调的,线性时间可解出答案. 大包子的束缚 题目描 ...
- 【BZOJ5495】[十二省联考2019]异或粽子(主席树,贪心)
[BZOJ5495][十二省联考2019]异或粽子(主席树,贪心) 题面 BZOJ 洛谷 题解 这不是送分题吗... 转异或前缀和,构建可持久化\(Trie\). 然后拿一个堆维护每次的最大值,每次如 ...
- 【BZOJ5496】[十二省联考2019]字符串问题(后缀树)
[BZOJ5496][十二省联考2019]字符串问题(后缀树) 题面 BZOJ 洛谷 题解 首先显然可以把具有支配关系的串从\(A\)到\(B\)连一条有向边,如果\(B_i\)是\(A_j\)的前缀 ...
- 【BZOJ5498】[十二省联考2019]皮配(动态规划)
[BZOJ5498][十二省联考2019]皮配(动态规划) 题面 BZOJ 洛谷 题解 先考虑暴力\(dp\),设\(f[i][j][k]\)表示前\(i\)所学校,有\(j\)人在某个阵营,有\(k ...
- [九省联考2018]秘密袭击coat
[九省联考2018]秘密袭击coat 研究半天题解啊... 全网几乎唯一的官方做法的题解:链接 别的都是暴力.... 要是n=3333暴力就完了. 一.问题转化 每个联通块第k大的数,直观统计的话,会 ...
随机推荐
- day01 --class --home
# 1.简述变量命名规范# 2.name = input(“>>>”) name变量是什么数据类型?# 3.if条件语句的基本结构? # 4.用print打印出下面内容:# ⽂能提笔 ...
- Java中常见的jar包及其主要用途
jar包 用途 axis.jar SOAP引擎包 commons-discovery-0.2.jar 用来发现.查找和实现可插入式接口,提供一些一般类实例化.单件的生命周 ...
- STM32-RS232通信软硬件实现
OS:Windows 64 Development kit:MDK5.14 IDE:UV4 MCU:STM32F103C8T6/VET6 AD:Altium Designer 18.0.12 1.RS ...
- Linux Shell编程、变量、控制语句
为什么要学习Shell编程 1)Linux运维工程师在进行服务器集群管理时,需要编写Shell程序来进行服务器管理. 2)对于JavaEE和Python程序员来说,工作的需要,你的老大会要求你编写一些 ...
- Django中的Session--实现登录
Django中的Session--实现登录 Django Session Session Session 是什么 Session保存在服务端的键值对. 为什么要有 Session Cookie 虽然 ...
- Linus' Law
Given enough eyeballs, all bugs are shallow. ------埃里克 ...
- JS检测数据类型
如果你要判断的是基本数据类型或JavaScript内置对象,使用toString: 如果要判断的时自定义类型,请使用instanceof. 1.typeof typeof操作符返回的是类型字符串,它的 ...
- ubuntu编译安装protobuf
测试环境:Ubuntu 16.04 LTS 到protobuf的release页面 下载源码:https://github.com/protocolbuffers/protobuf/releases/ ...
- CentOS6.4将MySQL5.1升级至5.5.36
1.为了安全期间,首先需要备份原有数据 2.卸载原有MySQL,先停止原有的MySQL服务,再查找 find / -name mysql [root@qxyw /]# find / -name mys ...
- 2-9 js基础 cookie封装
// JavaScript Document 'use strict'; function setCookie(sName,sValue,iDay){ if(iDay){ var oDate = ne ...