被巨佬锤爆了啊啊啊啊
脑力不足很多比较简单的东西想不到
国赛出现类似的知识点概率不大 但还是订正一下吧
冰火战士(A/B卷)
我考场写的垃圾代码竟然能过 感谢少爷机
可以将冰与火做差 得到的函数单调不降
找到最大的差值小于零的温度t 还得和t+1的答案比较谁更优
然后还得往右移(好像这里暴力往右移得满分?)
然后常数就大爆炸……
其实树状数组也是可以一个log二分的(可惜我不会)如果写了这个就能稳过了 还可以有点剪枝啥的
遇到这种2e6还无O2的毒瘤题就千万别用set和map了……
#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);i<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);i>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
void wr(int x){
if(x<0)x=-x,putchar('-');
if(x>=10)wr(x/10);
putchar('0'+x%10);
}
const int MAXN=2e6+10;
struct node{
int op,t,x,y;
}q[MAXN];
int Q,buc[MAXN],sz,sum;
struct BIT{
int c[MAXN];
inline int ask(int i){
int res=0;
for(;i;i^=i&-i)
res+=c[i];
return res;
}
inline void add(int i,int v){
for(;i<=sz;i+=i&-i)
c[i]+=v;
}
}T[2];
inline int gval(int x){
int l=1,r=sz,res=sz+1;
while(l<=r){
int mid=l+r>>1;
if(buc[mid]>=x)r=mid-1,res=mid;
else l=mid+1;
}
return res;
}
main(){
Q=read();
fp(i,1,Q){
q[i].op=read(),q[i].t=read();
if(q[i].op==1){
buc[++sz]=q[i].x=read();q[i].y=read();
}
}
sort(buc+1,buc+1+sz);sz=unique(buc+1,buc+1+sz)-buc-1;
fp(i,1,Q)q[i].x=gval(q[i].x);
fp(i,1,Q){
int op=q[i].op,tt=q[i].t,x,y;
if(op==1)x=q[i].x,y=q[i].y;
else x=q[tt].x,y=-q[tt].y,tt=q[tt].t;
if(tt==0)T[0].add(x,y);
else sum+=y,T[1].add(x+1,y);
int val=-sum,p=0;
fd(j,20,0)if(p+(1<<j)<=sz){
int t=val+T[0].c[p+(1<<j)]+T[1].c[p+(1<<j)];
if(t<0)val=t,p+=1<<j;
}
int res1=2*T[0].ask(p),ans=res1;
if(p<sz){
int res2=2*(sum-T[1].ask(p+1));
if(res2>=res1){
ans=res2;
int tar=T[1].ask(p+1);
p=0;val=0;
fd(j,20,0)if(p+(1<<j)<=sz){
int t=val+T[1].c[p+(1<<j)];
if(t<=tar)val=t,p+=1<<j;
}
}
}
if(!ans)puts("Peace");
else wr(buc[p]),putchar(' '),wr(ans),putchar('
');
}
return 0;
}
组合数问题(A卷)
大佬口中的傻逼题 我就怎么都不会
肯定是先化成(sum_{i=0}^{m}a_isum_{k=0}^{n}k^ix^kinom{n}{k})
重点是对于(iin[0,m])求出(f_i=sum_{k=0}^nk^ix^kinom{n}{k})
以下做法基本都是从别的地方抄的
做法一:
(i=0)的情况是二项式定理 (f_i)等于(f_{i-1})求个导什么的
sto szq orz
做法二:
其实看到幂函数就应该想到斯特林数的 我连这一步都没做到
这里(S(i,j))表示第二类斯特林数
做法三:
求(f_i)的EGF(F(z))
如果模数取998244353可以做到(O(mlog m))
做法四:
考虑(f_i)组合意义:有(n)个不同的盒子 从这些盒子中选出(k)个 给这(k)个盒子染上(x)种颜色 把(i)个有标号的球放入(k)个盒子中
每个盒子的EGF(F(z))为(1+xe^z) 相当于枚举它是否被选中,放入了几个球
好像和上一个做法一模一样
#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);i<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);i>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
#define int long long
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
void wr(int x){
if(x<0)x=-x,putchar('-');
if(x>=10)wr(x/10);
putchar('0'+x%10);
}
const int MAXN=1010;
int S[MAXN][MAXN],n,X,mod,m,a[MAXN],pw[MAXN];
inline void tmod(int &x){x%=mod;}
inline int qpow(int a,int b){
int res=1;a%=mod;
for(;b;b>>=1,tmod(a*=a))
if(b&1)tmod(res*=a);
return res;
}
main(){
n=read();X=read();mod=read();m=read();
fp(i,0,m)a[i]=read();
S[0][0]=1;pw[0]=qpow(X+1,n-m);
fp(i,1,m)pw[i]=pw[i-1]*(X+1)%mod;
fp(i,1,m){
S[i][1]=1;
fp(j,2,i)
tmod(S[i][j]=S[i-1][j-1]+j*S[i-1][j]);
}
int ans=0;
fp(i,0,m){
int tx=1,tn=1,sum=0;
fp(j,0,i){
tmod(sum+=S[i][j]*tn%mod*tx%mod*pw[m-j]);
tmod(tn*=n-j);tmod(tx*=X);
}
tmod(ans+=sum*a[i]);
}
printf("%lld
",ans);
return 0;
}
魔法商店(A卷)
我怎么可能会…… 以后有时间再做吧
信号传递(A/B卷)
把一次传递的代价分摊给两个信号塔
设(g_S)表示用完(S)这个集合填充前(|S|)个位置的最小代价
需要预处理一个(h_{i,S})表示从(g_S)转移到(g_{Scup i})花费的额外代价
然后就会被卡空间……
考虑把(h_{i,S})折半 就同时优化了空间复杂度和时间上的常数(我怎么还是想不到)
#include<cstdio>
#include<cstring>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];i;i=e[i].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
//#define int long long
inline bool isdigit(char ch){return ch>='0'&&ch<='9';}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
void wr(int x){
if(x<0)x=-x,putchar('-');
if(x>=10)wr(x/10);
putchar('0'+x%10);
}
int g[1<<23],n,m,K,s[100010],cur;
int E[25][25],a[23][1<<12],b[23][1<<11];short bcnt[1<<23],lg[1<<23];
inline int min(int x,int y){return x<y?x:y;}
main(){
n=read();m=read();K=read();
fp(i,1,n){
s[i]=read()-1;if(i==1)continue;
++E[s[i-1]][s[i]];
}
int c1=(m+1)/2,s1=(1<<c1)-1,c2=m-c1,s2=(1<<c2)-1;
int S=(1<<m)-1;
fp(i,2,S)lg[i]=lg[i/2]+1;
fp(i,1,S)bcnt[i]=bcnt[i^(i&-i)]+1;
fp(i,0,m-1){
fp(j,0,s1){
fp(k,0,c1-1)if(k!=i){
if(!(j>>k&1))a[i][j]+=E[k][i]*K-E[i][k];
else a[i][j]+=E[k][i]+E[i][k]*K;
}
}
if(!c2)continue;
fp(j,0,s2){
fp(k,c1,m-1)if(k!=i){
if(!(j>>k-c1&1))b[i][j]+=E[k][i]*K-E[i][k];
else b[i][j]+=E[k][i]+E[i][k]*K;
}
}
}
fp(i,1,S)g[i]=1e9;
fp(i,0,S){
int tt=S^i,p=bcnt[i]+1;
for(;tt;tt^=(tt&-tt)){
int t=lg[tt&-tt],s=i|(1<<t);
g[s]=min(g[s],g[i]+(a[t][i&s1]+b[t][i>>c1])*p);
}
}
printf("%d
",g[S]);
return 0;
}
树(A卷)
又是巨佬口中的辣鸡题 又有相当多的做法(但我就是一个都想不到)
trie树那个做法的trick我考前还刚刚看过 结果不知道为什么压根没想到
做法一
我们要支持
- 全局加一
- 合并
- 插入
可以维护一个从低位到高位的trie 复杂度(O(nlog n)) 不过据说干不过两个log
做法二
拆开每一位计算贡献 假设这个点点权是(v) 好像可以发现(v,v+1,v+2,cdots)有循环节
可以找出贡献区间 然后发现它们模(2^{k+1})意义下是同一段区间
然后用天天爱跑步的技巧 dfs
时出来的数组值异或上刚进入时的数组值
复杂度也可以做到(O(nlog n)) 但常数极小
理解不是很透彻…… 可以看看这个:https://www.luogu.com.cn/blog/dengyaotriangle/solution-p6623
有加一操作! 我trie深度还设小了
#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];i;i=e[i].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
void wr(int x){
if(x<0)x=-x,putchar('-');
if(x>=10)wr(x/10);
putchar('0'+x%10);
}
const int MAXN=525020;
int n,val[MAXN],ch[MAXN*30][2],res[MAXN*30],tot;
bool vis[MAXN*30];
int rt[MAXN],fa[MAXN];long long ans;
vector<int>son[MAXN];
#define ls ch[o][0]
#define rs ch[o][1]
inline void pushup(int o,int d){
res[o]=res[ls]^res[rs]^(vis[rs]?1<<d:0);
}
void add(int &o,int x,int d){
if(!o)o=++tot;
vis[o]^=1;
if(d<21)add(ch[o][x>>d&1],x,d+1);
pushup(o,d);
}
void merge(int &o1,int o2,int d){
if(!o1||!o2)return void(o1|=o2);
vis[o1]^=vis[o2];
merge(ch[o1][0],ch[o2][0],d+1);merge(ch[o1][1],ch[o2][1],d+1);
pushup(o1,d);
}
void add1(int o,int d){
if(!o)return;swap(ls,rs);
if(d<21)add1(ls,d+1);
pushup(o,d);
}
void dfs(int u){
for(int v:son[u]){
dfs(v);merge(rt[u],rt[v],0);
}
add1(rt[u],0);add(rt[u],val[u],0);
ans+=res[rt[u]];
}
main(){
n=read();
fp(i,1,n)val[i]=read();
fp(i,2,n){
fa[i]=read();son[fa[i]].push_back(i);
}
dfs(1);
printf("%lld
",ans);
return 0;
}
作业题(A卷)
有一次我不会求生成树边权和 $gg告诉了我
然后我考场上又忘记了如何求生成树边权和 写了和那一次一摸一样的暴力……
我怎么都不知道(varphi = mu * Id) 当时用(mu)写的……
(F(d))表示只选取d倍数边的生成树边权和
重点是求(F)
将每条边边权设为(wx+1) 答案是一次项系数
对于除法:((1-ax)^{-1} equiv 1+ax pmod{x^2})
((ax+b)^{-1} equiv -frac{a}{b^2}x+b^{-1} pmod{x^2})
这时我们开始怀疑复杂度是不是跟暴力枚举每条边一样也是(O(n^3md))的 因为可能总共有(md)个位置需要算一遍行列式
我们只需要对边数(ge n-1)的(d)求行列式 这样复杂度最高是(O(n^4d)的)
我简直是重度脑残…… 先是没开long long 又是线性筛写错 不知道干什么了
UPD:之前的代码被hack了 现在应该没问题
#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];i;i=e[i].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
#define int long long
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
void wr(int x){
if(x<0)x=-x,putchar('-');
if(x>=10)wr(x/10);
putchar('0'+x%10);
}
const int mod=998244353,MAXW=152510;
inline void tmod(int &x){x%=mod;}
inline int qpow(int a,int b){
int res=1;a%=mod;
for(;b;b>>=1,tmod(a*=a))
if(b&1)tmod(res*=a);
return res;
}
inline int inv(int x){return qpow(x,mod-2);}
int phi[MAXW],p[MAXW/5],pcnt;
bool np[MAXW];
inline void sieve(int n){
phi[1]=np[1]=np[0]=1;
fp(i,2,n){
if(!np[i])p[++pcnt]=i,phi[i]=i-1;
fp(j,1,pcnt){
int t=p[j]*i;if(t>n)break;
np[t]=1;if(i%p[j]==0){phi[t]=phi[i]*p[j];break;}
phi[t]=phi[i]*(p[j]-1);
}
}
}
struct pr{
int a,b;
inline pr(int x=0,int y=0){a=x,b=y;}
inline operator bool()const{return a||b;}
inline pr &operator+=(const pr&x){tmod(a+=x.a);tmod(b+=x.b);return *this;}
inline pr &operator-=(const pr&x){tmod(a-=x.a);tmod(b-=x.b);return *this;}
inline pr &operator*=(const pr&x){tmod(a=a*x.b+b*x.a);tmod(b*=x.b);return *this;}
inline pr &operator/=(const pr&x){
int t=inv(x.b);
tmod(b*=t);tmod(a=(a-b*x.a)%mod*t);
return *this;
}
}g[32][32];
inline pr operator * (pr x,pr y){return {(x.a*y.b+x.b*y.a)%mod,x.b*y.b%mod};}
inline int det(int n){
pr res=pr(0,1);int fl=1;
fp(i,1,n){
int p=0;
fp(j,i,n)if(g[j][i]){p=j;break;}
if(!p)return 0;
if(p!=i)swap(g[p],g[i]),fl*=-1;
res*=g[i][i];
fp(j,i+1,n){
pr t=g[j][i];t/=g[i][i];
fp(k,i,n)g[j][k]-=t*g[i][k];
}
}
return fl*res.a;
}
int fa[32],n,qu[1010],qv[1010],qw[1010],m;
int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
inline void merge(int a,int b){
a=getf(a);b=getf(b);if(a==b)return;
fa[a]=b;
}
vector<int>bb[MAXW];
inline void Add(int x,int y,int w){
g[x][y]-=pr(w,1);g[y][x]-=pr(w,1);
g[x][x]+=pr(w,1);g[y][y]+=pr(w,1);
}
inline int solve(int p){
if(bb[p].size()<n-1)return 0;
fp(i,1,n)fa[i]=i;
for(auto i:bb[p])merge(qu[i],qv[i]);
int cc=0;fp(i,1,n)if(fa[i]==i)++cc;if(cc!=1)return 0;
mem(g);
for(auto i:bb[p])Add(qu[i],qv[i],qw[i]);
return det(n-1);
}
inline void insert(int w,int id){
fp(i,1,w){
if(i*i>w)return;if(w%i)continue;
bb[i].push_back(id);if(i!=w/i)bb[w/i].push_back(id);
}
}
main(){
n=read();m=read();sieve(MAXW-3);
fp(i,1,m){
qu[i]=read(),qv[i]=read();qw[i]=read();
insert(qw[i],i);
}
int ans=0;
fp(i,1,MAXW-3){
tmod(ans+=phi[i]*solve(i));
}
printf("%lld
",(ans+mod)%mod);
return 0;
}