初二下 合集
TREES OF TRANQUILLITY
考虑第一棵树肯定是选整条链。把第二棵树的限制搞成dfs序区间,因为这种区间要么不相交要么完全覆盖,所以用线段树维护一下就好了。
具体地,把每个点的区间赋值成这个点,然后下面的点:
- 如果发现自己[in,out]的位置已经有人了,那就把上面那个人干掉,自己加进去。
- 如果不冲突,直接塞进去。
- 如果自己覆盖了别人,那就不加自己了。
注意不要每次memset(t<=3e5),重新build的时候要把lazy标记清空!
#include<bits/stdc++.h>
using namespace std;
int t,n;
const int N=6e5+5;
vector<int> g[N],e[N];
int en[N],ot[N],cnt;
void dfs2(int u,int fa){
int i;en[u]=++cnt;
for(i=0;i<e[u].size();i++){
int v=e[u][i];
if(v!=fa)dfs2(v,u);
}
ot[u]=cnt;
}
int ans,res;
typedef long long ll;
struct node{
int l,r;
ll sum,lazy;
};
struct SegmentTree{
node tr[1200005];
void build(int k,int l,int r){
tr[k].l=l,tr[k].r=r;
if(l==r){
tr[k].sum=0;
tr[k].lazy=0;
return ;
}
int mid=l+r>>1;
build(k<<1,l,mid),build(k<<1|1,mid+1,r);
tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
tr[k].lazy=0;
}
void push_down(int k,int l,int r){
tr[k<<1].lazy+=tr[k].lazy,tr[k<<1|1].lazy+=tr[k].lazy;
tr[k<<1].sum+=tr[k].lazy*(tr[k<<1].r-tr[k<<1].l+1);
tr[k<<1|1].sum+=tr[k].lazy*(tr[k<<1|1].r-tr[k<<1|1].l+1);
tr[k].lazy=0;
}
void modifly(int k,int l,int r,int L,int R,ll v){
if(l>=L&&r<=R){
tr[k].sum+=(r-l+1)*v;
tr[k].lazy+=v;
return ;
}
push_down(k,l,r);
int mid=l+r>>1;
if(R<=mid)modifly(k<<1,l,mid,L,R,v);
else if(L>mid)modifly(k<<1|1,mid+1,r,L,R,v);
else modifly(k<<1,l,mid,L,mid,v),modifly(k<<1|1,mid+1,r,mid+1,R,v);
tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
}
ll query(int k,int l,int r,int L,int R){
if(l>=L&&r<=R){return tr[k].sum;}
push_down(k,l,r);
int mid=l+r>>1;
ll s=0;
if(R<=mid)s=query(k<<1,l,mid,L,R);
else if(L>mid)s=query(k<<1|1,mid+1,r,L,R);
else s=query(k<<1,l,mid,L,mid)+query(k<<1|1,mid+1,r,mid+1,R);
return s;
}
}t1,t2;
void dfs1(int u,int fa){
int i;
ll s=t1.query(1,1,n,en[u],ot[u]);
ll t=t2.query(1,1,n,en[u],ot[u]);
ll f1=0,f2=0;
if(s){
if(ot[u]-en[u]+1==s){
ll w=t/s;
t1.modifly(1,1,n,en[w],ot[w],-1ll);
t2.modifly(1,1,n,en[w],ot[w],-w);
f1=w;
t1.modifly(1,1,n,en[u],ot[u],1ll);
t2.modifly(1,1,n,en[u],ot[u],u);
f2=u;
}
}else{
t1.modifly(1,1,n,en[u],ot[u],1ll);
t2.modifly(1,1,n,en[u],ot[u],u);
f2=u;ans++;
}
for(i=0;i<g[u].size();++i){int v=g[u][i];if(v!=fa)dfs1(v,u);}
res=max(res,ans);
if(f1&&f2){
t1.modifly(1,1,n,en[f1],ot[f1],1ll),t2.modifly(1,1,n,en[f1],ot[f1],f1);
t1.modifly(1,1,n,en[f2],ot[f2],-1ll),t2.modifly(1,1,n,en[f2],ot[f2],-f2);
}else if(f2)
t1.modifly(1,1,n,en[f2],ot[f2],-1ll),t2.modifly(1,1,n,en[f2],ot[f2],-f2),ans--;
}
int main(){
int i;
cin>>t;
while(t--){
scanf("%d",&n);
int i;
for(i=1;i<=n;++i)g[i].clear(),e[i].clear();
cnt=0,res=0,ans=0;
for(i=2;i<=n;++i){
int x;
scanf("%d",&x);
g[x].push_back(i),g[i].push_back(x);
}
for(i=2;i<=n;++i){
int x;
scanf("%d",&x);
e[x].push_back(i),e[i].push_back(x);
}
t1.build(1,1,n),t2.build(1,1,n);
dfs2(1,0),dfs1(1,0);
printf("%d\n",res);
}
return 0;
}
YSOI2020 换寝室
一开始不知道树形dp怎么搞,后面看了cxy题解之后恍然大悟。
二分极差,判断能否使代价小于等于k
f[i][j]:以i为根的子树,i所在的连通块值域为[aj,aj+x]
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int N=2005;
const int M=1000005;
int cnt,nxt[N],to[N],h[N];
int a[N],d[N],b[M][2];
void add(int x,int y){
cnt++;
nxt[cnt]=h[x];
h[x]=cnt;
to[cnt]=y;
}
typedef long long ll;
int f[N][15],dep[N];
ll s[N],dp[N][N];
void dfs(int u,int fa){
f[u][0]=fa,dep[u]=dep[fa]+1;int i;
for(i=h[u];i;i=nxt[i]){
int v=to[i];
if(v!=fa)dfs(v,u);
}
}
int lca(int x,int y){
if(dep[x]>dep[y])swap(x,y);int i;
for(i=10;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];
if(x==y){return x;}
for(i=10;i>=0;i--)if(f[y][i]!=f[x][i])y=f[y][i],x=f[x][i];
return f[x][0];
}
void dfs2(int u,int fa){
int i;s[u]=1ll*d[u];
for(i=h[u];i;i=nxt[i]){
int v=to[i];
if(v!=fa)dfs2(v,u),s[u]+=s[v];
}
}
ll qz[N][N],hz[N][N];
void dfs3(int u,int fa,int x){
int i,j;//f[i][j]=sgm(min(f[son][!j]+edge(u,v),f[son][j]))
for(i=h[u];i;i=nxt[i]){
int v=to[i];
if(v!=fa){
dfs3(v,u,x); qz[v][0]=1e9+1,hz[v][n+1]=1e9+1;
for(j=1;j<=n;j++)qz[v][j]=min(qz[v][j-1],dp[v][j]);
for(j=n;j>=1;j--)hz[v][j]=min(hz[v][j+1],dp[v][j]);
for(j=1;j<=n;j++)
dp[u][j]+=min(min(qz[v][j-1],hz[v][j+1])+s[v],dp[v][j]);
}
}
for(int j=1;j<=n;j++)
if(!(a[u]>=a[j]&&a[u]<=a[j]+x))dp[u][j]=1e9+1;
}
int check(int x){
memset(dp,0,sizeof(dp));
memset(qz,0,sizeof(qz));
memset(hz,0,sizeof(hz));
dfs3(1,0,x);
for(int i=1;i<=n;i++)
if(dp[1][i]<=k){return 1;}
return 0;
}
int main(){
register int i,j;
cin>>n>>m>>k;
for(i=1;i<=n;++i)scanf("%d",&a[i]);
for(i=1;i<n;++i){
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,0);
for(j=1;j<=10;++j)for(i=1;i<=n;++i)f[i][j]=f[f[i][j-1]][j-1];
for(i=1;i<=m;++i){
scanf("%d%d",&b[i][0],&b[i][1]);
d[b[i][0]]++,d[b[i][1]]++;
d[lca(b[i][0],b[i][1])]-=2;
}
dfs2(1,0);
int l=0,r=1e9,ans;
while(l<=r){
int mid=l+r>>1;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<endl;
return 0;
}
NOI2021 题解/同步赛游记
一开始以为挺难的,还是犯了畏难的老毛病,一定要杜绝
不要给我面向数据编程
轻重边
70分,容易发现,如果a-b 存在,一定有 $ dfn[lst[a]]==dfn[lst[b]]$ 即最后一次操作到a,b的时间戳相同
100分
也就是说,a-b 路径上的连续段,都要满足上述条件。不断做区间覆盖,将一段路颜色改成当前时间,询问也就是统计颜色段个数。
所以场上有人拉了[SDOI 染色]的板子,过了大样例
不得不说,这个性质太妙了
庆典
对于这个特殊的有向图,可以对其尝试缩点,发现一定会缩成外向树。
k<2的64分就搞定了
k=2,场上我是大力分讨的,但后面因为情况太多弃了
量子通信
256=16*16,进行分块,而k<=15,所以肯定有一个整块要完全相同,然而数据均匀随机,所以相同的16×400000/65536
差不多100个,对于这些串,暴力bitset判断即可
剩下的题都不会了,我还是太菜了
这篇游记,可以说是自CSP2019之后写的字数最多的了
Day1
晚上没睡好。
早上一大早挣扎着起来,发现没开???
日
睡了一会,tm睡着了。
开考20分钟的时候才醒
中间还浪费了不少时间,总共加起来有一个多小时
T1
50分不是傻逼吗,想想70
不会啊70
很久以后才决定写50
中间爆栈了,我还调了很久,日!
调了整整3h啊啊啊啊
T2
看错题了www
白耗了2h,fuck
0分,日!
T3
1-7 都是傻逼
但好像一颗外向树的k=2有点难写
只能拿44分
算了,只剩25分钟了,老子不写了
就28分得了,日!
怎么说呢,T1 100分不会做的确是我能力不够,但是70分真的是水得不能再水了,为啥我B性质拿不到呢???
赛后重新看了一眼2
原来我能写n<=10且有B性质的40分,日!
为什么就没想到状压,然后把每层答案乘起来呢???
T3缩点之后有这么好的性质,我也没想到
最近脑子一直不清醒啊,必须赶紧把状态调整过来!
50+0+28=78
Day 2
T1
做法很早就想到了,但是没看到数据随机,以为自己是暴力分,而且恰好空间开小了导致第3个大样例过不了,就一直以为自己不会做。
(后来问了一位神仙才发现自己原来是对的)
然后迅速切掉了
T2
不会做啊,完全不会做啊啊啊啊啊啊
T3
一开始有点思路,后来一直调不出来,才发现假掉了。。。
100+0+5=105
事实证明面向数据编程会想错
真正的强者,一定是脑子中不断冒出做法,深度思考后,再去看自己能过几分的
我:78+88=166
人均:70+40+44+100+20+36=310
神仙:100+100+100+100+70+100=570
看到了自己与众人的差距
EZEC-10 序列
考虑 \(X\ \operatorname{xor}\ Y = Z\)
等价于 \(X\ \operatorname{xor}\ Z = Y\)
等价于 \(Y\ \operatorname{xor}\ Z = X\)
于是建图,连(x,y,z)的无向边。BFS,对于每个连通块,如果一路异或到某个点的Z有两种,那显然矛盾,puts("0")退出
从rt点BFS时,把扩展到的每个点进行一遍可乐,求出rt点值的取值区间,乘起来就行。
但是值域有1e9,求出取值区间需要差分+离散化,但是这样就2log了。所以直接基数排序。
(前面写的常数太大,拿cxy的代码调了一下午)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod 1000000007
#define N 500005
int n,m,K,cnt,h[N<<1],nxt[N<<1],to[N<<1],val[N<<1],yh[N];
inline void add(int x,int y,int z){
cnt++;
nxt[cnt]=h[x];
h[x]=cnt;
to[cnt]=y,val[cnt]=z;
}
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
char ch=nc();
int sum=0;
while(!(ch>='0'&&ch<='9'))ch=nc();
while(ch>='0'&&ch<='9')sum=(sum<<1)+(sum<<3)+(ch^48),ch=nc();
return sum;
}
queue<int> q;
int tot,vis[N],p;
int R[256];
struct node{
int x,y;
bool operator <(const node u)const{
return x<u.x;
}
}P[20000005],t[20000005];
ll ans=1;
inline void cola(int v){
int cur=0;
register int i;
for(i=29;i>=0;--i){
if((K>>i)&1){
int tmp=!((v>>i)&1);
tmp=tmp<<i,cur+=tmp;
}else{
int tmp=!((v>>i)&1);
tmp=tmp<<i;
t[++p]=(node){cur+tmp,1};
t[++p]=(node){cur+tmp+(1<<i),-1};
tmp=(v>>i)&1,tmp=tmp<<i,cur+=tmp;
}
}
}
inline void bfs(int x){
q.push(x);
register int i,u,v,w;
vis[x]=1,yh[x]=0;cola(0);
while(!q.empty()){
u=q.front();q.pop();
for(i=h[u];i;i=nxt[i]){
v=to[i],w=val[i];
if(!vis[v])yh[v]=yh[u]^w,vis[v]=1,tot++,cola(yh[v]),q.push(v);
else if(yh[v]!=(yh[u]^w)){
puts("0");
exit(0);
}
}
}
}
inline void radix_sort(){
register int i;
for(i=1;i<=p;++i)R[t[i].x&255]++;
for(i=1;i<=255;++i)R[i]+=R[i-1];
for(i=p;i>=1;--i)P[R[t[i].x&255]--]=t[i];
for(i=1;i<=p;++i)t[i]=P[i];
for(i=0;i<=255;++i)R[i]=0;
for(i=1;i<=p;++i)R[(t[i].x>>8)&255]++;
for(i=1;i<=255;++i)R[i]+=R[i-1];
for(i=p;i>=1;--i)P[R[(t[i].x>>8)&255]--]=t[i];
for(i=1;i<=p;++i)t[i]=P[i];
for(i=0;i<=255;++i)R[i]=0;
for(i=1;i<=p;++i)R[(t[i].x>>16)&255]++;
for(i=1;i<=255;++i)R[i]+=R[i-1];
for(i=p;i>=1;--i)P[R[(t[i].x>>16)&255]--]=t[i];
for(i=1;i<=p;++i)t[i]=P[i];
for(i=0;i<=255;++i)R[i]=0;
for(i=1;i<=p;++i)R[(t[i].x>>24)&255]++;
for(i=1;i<=255;++i)R[i]+=R[i-1];
for(i=p;i>=1;--i)P[R[(t[i].x>>24)&255]--]=t[i];
for(i=1;i<=p;++i)t[i]=P[i];
for(i=0;i<=255;++i)R[i]=0;
}
int res;
int main(){
n=read(),m=read(),K=read();
register int i,j,x,y,z;
for(i=m;i;--i){
x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}int tt;
for(i=1;i<=n;++i){
if(vis[i])continue;
tot=p=res=tt=0;
bfs(i);t[++p]=(node){0,0};
if(p<=512)sort(t+1,t+p+1);
else radix_sort();
for(j=1;j<=p;++j){
tt+=t[j].y;
if(tt==0){
if(j!=p)res+=t[j+1].x-t[j].x;
else res+=1073741823-t[j].x+1;
}
}
ans=ans*(1ll*res)%mod;
}
printf("%lld",ans);
return 0;
}
牛客挑战赛51
A sbt,不讲
B 巧妙的并查集
很显然有一种贪心做法,每次都尽量从最大的[E]集合加到第二大的集合
C 数位dp+二分
二分一下y,用数位dp判定[x,y]中是否有>=k个回文数
D 不会
E 好题
https://ac.nowcoder.com/acm/contest/11191/E
考虑通过欧拉函数拆开来,统计一下三元环的贡献i就行了。
挑战赛 ×
数学赛 √
看来Math-Round的质量还挺高的嘛
牛客周赛26
只会A www
手玩几遍,发现交换两个数奇偶性不变,剩下三个操作直接拆解掉就行了,SBT
想到一个比较和谐的性质,但还是不会DP啊。。。
先咕着,之后再写
BZOJ4771七彩树
不是原题,是LJ考试一道类似的题
给定一棵以结点1为根的树,每个点有颜色\(C_i\)。每次询问给定u,d,你要求出在以u为根的子树
内所有与u距离不超过d的点中不同颜色个数,强制在线。
如果没有距离限制,考虑如何计算答案(不用数据结构方法)
考虑每两个颜色相同的点U,V,lca(U,V)=W,能对哪些点答案产生贡献。显然,\(b_u++,b_v++,b_w --\) 但这么做会算重,所以对于每个U只计算它的前驱、后继作为V的贡献,具体地:
nik:版本,fk:U
update(root[nik],root[nik-1],1,1,n,dfn[fk]);
if(pre)nik++,update(root[nik],root[nik-1],-1,1,n,dfn[lca(pre,fk)]);
if(suc)nik++,update(root[nik],root[nik-1],-1,1,n,dfn[lca(fk,suc)]);
if(pre&&suc)nik++,update(root[nik],root[nik-1],1,1,n,dfn[lca(pre,suc)]);
最后点x子树的b和就是x点的答案。
但是有了距离限制,容易想到用bfs序来一个个加入节点到新主席树里,最后记录下每层最右边的那个节点的主席树版本号。
查询时,直接找U对应的版本的\(Sum[in[u],out[u]]\)
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=200005;
int cnt,h[N*2],nxt[N*2],to[N*2];
void add(int x,int y){
cnt++;
nxt[cnt]=h[x];
h[x]=cnt;
to[cnt]=y;
}
queue<int> q;
int bh[N],tot,rh[N],c[N],vis[N],dep[N],dfn[N],ot[N];
int f[N][21];
void init(){
memset(vis,0,sizeof(vis));
q.push(1),vis[1]=1;
while(!q.empty()){
int u=q.front();q.pop();
tot++;bh[tot]=u,rh[u]=tot;
for(int i=h[u];i;i=nxt[i]){
int v=to[i];
if(!vis[v])vis[v]=1,q.push(v);
}
}
}
int tim,mxd;
void dfs(int u,int fa){
dfn[u]=++tim;
f[u][0]=fa;dep[u]=dep[fa]+1;mxd=max(mxd,dep[u]);
for(int i=h[u];i;i=nxt[i]){
int v=to[i];
if(v!=fa)dfs(v,u);
}ot[u]=tim;
}
struct node{
int l,r,sum;
}T[N*40];
int root[N],pos[N];
void update(int &now,int pre,int val,int l,int r,int pos){
T[++cnt]=T[pre],T[cnt].sum+=val,now=cnt;
if(l==r) return;
int mid=l+r>>1;
if(pos<=mid) update(T[now].l,T[pre].l,val,l,mid,pos);
else update(T[now].r,T[pre].r,val,mid+1,r,pos);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&R>=r) return T[rt].sum;
int ans=0,mid=l+r>>1;
if(L<=mid)ans+=query(L,R,l,mid,T[rt].l);
if(R>mid)ans+=query(L,R,mid+1,r,T[rt].r);
return ans;
}
int lca(int x,int y){
int i;if(dep[x]>dep[y])swap(x,y);
for(i=20;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];
if(x==y)return x;
for(i=20;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
set<pair<int,int> > t[N];
int nik,bil[N];
int main(){
//freopen("cheese.in","r",stdin);
//freopen("cheese.out","w",stdout);
int tt;
cin>>tt;
while(tt--){
scanf("%d%d",&n,&m);
int i,j;cnt=0;nik=0,mxd=0,tot=0,tim=0,lastans=0;
memset(to,0,sizeof(to)),memset(nxt,0,sizeof(nxt)),memset(h,0,sizeof(h));
for(i=1;i<=n;i++)scanf("%d",&c[i]),t[i].clear();
for(i=2;i<=n;i++){
int x;scanf("%d",&x);
add(x,i),add(i,x);
}
init();dfs(1,0);
for(j=1;j<=20;j++)for(i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];
for(i=1;i<=n;i++){
int fk=bh[i],pre=0,suc=0;++nik;
pair<int,int> k=make_pair(dfn[fk],fk);
update(root[nik],root[nik-1],1,1,n,dfn[fk]);
set<pair<int,int> >::iterator v=t[c[fk]].lower_bound(k);
if(v!=t[c[fk]].end())suc=v->second;
if(v!=t[c[fk]].begin())v--,pre=v->second;
if(pre)nik++,update(root[nik],root[nik-1],-1,1,n,dfn[lca(pre,fk)]);
if(suc)nik++,update(root[nik],root[nik-1],-1,1,n,dfn[lca(fk,suc)]);
if(pre&&suc)nik++,update(root[nik],root[nik-1],1,1,n,dfn[lca(pre,suc)]);
bil[dep[fk]]=nik;t[c[fk]].insert(k);
}
while(m--){
int u,d;scanf("%d%d",&u,&d);
u^=lastans,d^=lastans;
int deep=min(dep[u]+d,mxd);
lastans=query(dfn[u],ot[u],1,n,root[bil[deep]]);
printf("%d\n",lastans);
}
}
return 0;
}
EZEC8 猜书(交互)题解
第一篇交互题题解,就当是在学交互题格式罢
这道题2000的数据量,1e5个询问,不用说一定是根号分治。
具体的,对于第i层(设有x个点)第i-1层(有y个)的点,两两之间询问一遍距离,但如果树很扁就会挂掉,那就对于\(x > \sqrt n\) 且 \(y > \sqrt n\)的那几层,上面的节点问一遍子树,盘掉就行了。易证这样询问数一定是不超过1e5的
通过这题,我发现IO交互要在每个printf后面加上fflush(stdout),没了。。。
#include<bits/stdc++.h>
using namespace std;
int n;
int dep[2005],depp,f[2005];
vector<int> depth[2005];
int read(){
int s=0,w=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-')w=-1;
c=getchar();
}
while(c>='0'&&c<='9')s=(s<<1)+(s<<3)+(c^48),c=getchar();
return s*w;
}
void ask1(int x,int y){
printf("? 1 %d %d\n",x,y);
fflush(stdout);
int d=read();
if(x==1)dep[y]=d,depth[d].push_back(y),depp=max(d,depp);
if(d==1)f[y]=x;
}
void ask2(int u){
printf("? 2 %d\n",u);
fflush(stdout);
int sz=read();
while(sz--){
int x=read();
if(dep[u]==dep[x]-1)f[x]=u;
}
}
int main(){
n=read();
int i,j,k;
for(i=2;i<=n;i++)ask1(1,i);
int w=sqrt(n);//根号分治
for(i=2;i<=depp;i++){
int sz1=depth[i].size(),sz2=depth[i-1].size();
if(sz1>w&&sz2>w){
for(j=0;j<sz2;j++)ask2(depth[i-1][j]);
}else{
for(j=0;j<sz1;j++){
for(k=0;k<sz2;k++){
ask1(depth[i-1][k],depth[i][j]);
}
}
}
}
putchar('!');
for(i=2;i<=n;i++)printf(" %d",f[i]);
fflush(stdout);
return 0;
}
NTT学习笔记
呼呼呼,因为要学多项式,终于被逼着学会了\(LaTeX\)
-
[模板]多项式乘法
#include<bits/stdc++.h> using namespace std; int n,m; const int N=4000005; typedef long long ll; const ll P=998244353,g=3,gi=332748118; ll a[N],b[N]; int lmt=1,L=0; int r[N]; ll qp(ll x,ll y){ ll res=1; while(y){ if(y&1)res=(res*x)%P; x=(x*x)%P; y>>=1; } return res; } void NTT(ll *A,int tp){ int i; for(i=0;i<lmt;i++)if(i<r[i])swap(A[i],A[r[i]]); for(int md=1;md<lmt;md<<=1){ ll omega=qp(tp==1?g:gi,(P-1)/(md<<1)); for(int j=0;j<lmt;j+=(md<<1)){ ll w=1; for(int k=0;k<md;k++,w=(w*omega)%P){ ll x=A[j+k],y=w*A[j+k+md]%P; A[j+k]=(x+y)%P,A[j+k+md]=(x+P-y)%P; } } } } int main(){ int i; cin>>n>>m; for(i=0;i<=n;i++)scanf("%lld",&a[i]),a[i]=(a[i]%P+P)%P; for(i=0;i<=m;i++)scanf("%lld",&b[i]),b[i]=(b[i]%P+P)%P; while(lmt<=n+m)lmt<<=1,L++; for(i=0;i<lmt;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(L-1)); NTT(a,1),NTT(b,1); for(i=0;i<lmt;i++)a[i]=(a[i]*b[i])%P; NTT(a,-1); ll rv=qp(lmt,P-2); for(i=0;i<=n+m;i++)printf("%lld ",(a[i]*rv)%P); return 0; }
你才知道我连FFT都没写过,直接背NTT模板的吗?
- **P6300 悔改**
设ai为长度为i的木棍的出现次数,则易得
$ Ans_k = \sum_{i+j=k}^{n} \min(a_i,a_j)$
后面这个min函数有点烦,怎么办呢?
提一个d出来,算d作为最小值,对$Ans_k$的贡献
$ \sum_{d=1}^{n} \sum_{i+j=k} [a_i>=d][a_j>=d]$
枚举这个d,离散化之后,$ \sum_{d=1}^{} = n $,所以
d是$ \sqrt{n}$级别的
于是,对于后面$\sum_{i+j=k}[a_i>=d][a_j>=d]$,这个直接NTT加容斥搞定了
代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=400005;
int a[N],r[N],b[N],limit=1,L=0;
typedef long long ll;
const ll P=998244353,g=3,gi=332748118;
ll c[N],rs[N];
inline int read(){
ll s=0,f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar(); }
while(c<='9'&&c>='0')s=(s<<1)+(s<<3)+(c^48),c=getchar();
return s*f;
}
inline ll qp(ll x,ll y){
ll res=1;
while(y){
if(y&1)res=(res*x)%P;
x=(x*x)%P;
y>>=1;
}
return res;
}
inline void NTT(ll *u,int v,int len){
for(int i=0;i<len;i++)if(i<r[i])swap(u[i],u[r[i]]);
for(int mid=1;mid<len;mid<<=1){
ll omega=qp(v==1?g:gi,(P-1)/(mid<<1));
for(int j=0;j<len;j+=(mid<<1)){
for(int k=0,w=1;k<mid;k++,w=(w*omega)%P){
int x=u[j+k],y=w*u[j+k+mid]%P;
u[j+k]=(x+y)%P,u[j+k+mid]=(x-y+P)%P;
}
}
}
}
ll ans,fans;
int main(){
register int i,j;
n=read(),m=read();
for(i=1;i<=n;++i)a[read()]++;
for(i=1;i<=m;++i)b[i]=a[i];
sort(b,b+m+1);
int q=unique(b,b+m+1)-b-1;
while(limit<=2*m)limit*=2,L++;
for(i=0;i<limit;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
for(i=1;i<=q;++i){
int w=b[i];
c[0]=0;
for(j=1;j<limit;++j){
if(a[j]>=w)c[j]=1;
else c[j]=0;
}
NTT(c,1,limit);
for(j=0;j<limit;++j)c[j]=c[j]*c[j]%P;
NTT(c,-1,limit);
ll inv=qp(limit,P-2);
for(j=1;j<=m*2;++j)
rs[j]+=1ll*(b[i]-b[i-1])*(c[j]*inv%P)%P;
}
for(i=1;i<=m*2;++i){
if(rs[i]/2>ans){
ans=rs[i]/2;
fans=i;
}
}
printf("%lld %lld\n",ans,fans);
return 0;
}
- 牛客OJ NC220167 简单题
调了一万年,至今未AC...
给出两个长度为n的非负整数序列\(a_1,a_2,\cdots ,a_n\)
和\(b_1,b_2,\cdots,b_n\) ,对每个 \(k\in[1,3n]\) 求\(C_k=\sum_{i+j+gcd(i,j)=k}a_ib_j\)
结果对998244353取模。
考虑计算每个d作为\(gcd(i,j)\) ,对各个k的贡献
仔细推一波,容斥一下
(感觉好多NTT题都要容斥啊)
- P5641 【CSGRound2】开拓者的卓识
反正算贡献了之后,推一波柿子就行了
令\(A_i=a_i*C(i+k-1,k-1),Bi=C(i+k-1,k-1)\)
会发现柿子可以化成\(\sum_{i+j=k}A_iB_j\)的形式
然后就是sbt了,代码:
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=4e5+5;
typedef long long ll;
ll k,a[N];
const ll P=998244353,g=3,gi=332748118;
ll b[N],c[N];
ll qp(ll x,ll y){
ll res=1;
while(y){
if(y&1)res=(res*x)%P;
x=(x*x)%P;
y>>=1;
}
return res;
}
ll lmt=1,L=0,r[N];
void NTT(ll *A,int ty){
int i;
for(i=0;i<lmt;i++)if(i<r[i])swap(A[i],A[r[i]]);
for(ll mid=1;mid<lmt;mid<<=1){
ll omega=qp(ty==1?g:gi,(P-1)/(mid<<1));
for(int j=0;j<lmt;j+=mid<<1){
ll w=1;
for(ll k=0;k<mid;k++,w=(w*omega)%P){
ll x=A[j+k],y=w*A[j+k+mid];
A[j+k]=(x+y)%P,A[j+k+mid]=(x+P-y)%P;
}
}
}
}
int main(){
int i;
cin>>n>>k;
c[0]=1;
for(i=1;i<n;i++)c[i]=c[i-1]*(i+k-1)%P*qp(i,P-2)%P;
for(i=0;i<n;i++){
scanf("%lld",&a[i]);
b[i]=c[i]*a[i]%P;
}
while(lmt<=n+n)lmt*=2,L++;
for(i=0;i<lmt;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
NTT(b,1),NTT(c,1);
for(i=0;i<lmt;i++)b[i]=b[i]*c[i]%P;
NTT(b,-1);
ll inv=qp(lmt,P-2);
for(i=0;i<n;i++)printf("%lld ",(b[i]*inv%P+P)%P);
return 0;
}
PMOI-3 题解
人太菜了,以后周2,4,7要补一点比赛题,写题解
T1 sbt
T3 dp+mobius
f[i][j]表示当前填到第i个数,填j的方案数。(n^2*m)
考虑到可以反演把gcd搞掉,同时xi互不相同,O(n*m)
把后面一坨式子用pr记下来,O(nlogm^2)
最后迪利克雷搞一搞前后缀和,可以搞到100pts
参考赛后题解
80pts
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=500005;
int pr[N],f[N],x[N];
int mu[N],pri[N],cnt,vis[N];
const int mod=998244353;
vector<int> g[N];
void init(){
int i,j;
mu[1]=1;
for(i=2;i<=m;i++){
if(!vis[i]){
pri[++cnt]=i;
mu[i]=-1;
}
for(j=1;j<=cnt&&pri[j]<=m/i;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0){
mu[i*pri[j]]=0;
break;
}
mu[i*pri[j]]=-mu[i];
}
}
for(i=1;i<=m;i++){
for(j=i;j<=m;j+=i){
g[j].push_back(i);
}
}
}
int main(){
int i,j,k;
cin>>n>>m;
for(i=1;i<n;i++)scanf("%d",&x[i]);
init();
for(i=x[1];i<=m;i+=x[1]){
for(j=0;j<g[i].size();j++)pr[g[i][j]]++;
}
for(i=1;i<n;i++){
for(j=1;j<=m/x[i];j++){
for(k=0;k<g[j].size();k++){
int w=g[j][k];
f[j*x[i]]=(f[j*x[i]]+mu[w]*pr[w*x[i]])%mod;
}
}
for(j=1;j<=m/max(1,x[i-1]);j++){
for(k=0;k<g[max(x[i-1],1)*j].size();k++){
pr[g[max(x[i-1],1)*j][k]]=0;
}
}
for(j=x[i];j<=m;j+=x[i]){
for(k=0;k<g[j].size();k++){
pr[g[j][k]]=(pr[g[j][k]]+f[j])%mod;
}
if(i!=n-1)f[j]=0;
}
}
int ans=0;
for(i=1;i<=m;i++)ans=(ans+f[i])%mod;
cout<<ans<<endl;
return 0;
}
T5:
很显然 ans=sum(a,b){e<F(L,d)-F(L,c)<f}
考虑将[b,c]段提出来,指针p b->a 移动,F(L,d)-F(L,c)是单调不增的,然后就可以二分
当F(L,d)-F(L,c)=e或f时,p可以到哪里。log^2主席树解决
ZJOI2021划水记
一般都是考完之后写的,这次看了1e4-1的游记,学他没考就写
NOIP参加了,分也不低,但是CSP分太低了所以只能是非正式。
因为初三有几个人想参加,但学校只有5个名额,我和5ab是到4月8日才被加进去的。希望别给学校丢脸吧。(话说最近比赛好多啊,又可以去boom0了呢,像NOIO一样)
Day -2(4.8) 来机房颓废,写作业,希望感冒赶快好。
Day -1 will赶作业
Day 1:
感冒没好,状态差得一匹
T1 没想到log做法,大概40-60
T2 m=2没想到 30
T3 16
不到三位数
Day 2:
感冒还没好。
T1 最后20mins写的,只写了30,早知道这题部分分那么好拿就去打了
T2 花了3h都没把2n×n×m的状压调好,就写了2n×n^2×m,mmp只能过80,全排列tm都能60
T3 写都没写
Day 3:
测了下冥间数据
最好情况也就200上下,不管了,搞whk去了,马上要期中考了希望能好些
送自己一句话:不要害怕任何题。

浙公网安备 33010602011771号