题意:n个节点的树,每个点有一个颜色,询问点u子树中距离不超过d的节点有多少种不同的颜色
题解:按深度依次插入节点u,用set维护它dfs序相邻的同色点l,r,
再对每一个深度开一个动态开点线段树,把u对应的dfs序位置 u+1,LCA(u,l)-1,LCA(u,r)-1,LCA(l,r)+1
实际上就是一个lca去重,查询的时候直接查dep[u]+d对应深度的线段树中u子树对应的区间
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define N 100005
int col[N],fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int fa[N],son[N],top[N],siz[N],dep[N],dfn[N],num[N],dc;
vector<int> G[N];int mxd;
void dfs1(int u)
{
dep[u]=dep[fa[u]]+1;siz[u]=1;
mxd=max(mxd,dep[u]);
G[dep[u]].push_back(u);
for(int v,p=fir[u];p;p=nxt[p]){
if((v=to[p])!=fa[u]){
fa[v]=u;dfs1(v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v])
son[u]=v;
}
}
}
void dfs2(int u)
{
dfn[u]=++dc;num[dc]=u;
if(son[u])top[son[u]]=top[u],dfs2(son[u]);
for(int v,p=fir[u];p;p=nxt[p])
if((v=to[p])!=fa[u]&&v!=son[u])
top[v]=v,dfs2(v);
}
int LCA(int x,int y)
{
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
set<int> S[N];
set<int>::iterator it1,it2,edi;
struct node{
int l,r,x;
}a[N<<5];
int T[N],tot;
void insert(int &i,int l,int r,int x,int k,int pre)
{
if(i==pre)i=++tot,a[i]=a[pre];
a[i].x+=k;
if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)insert(a[i].l,l,mid,x,k,a[pre].l);
else insert(a[i].r,mid+1,r,x,k,a[pre].r);
}
int query(int i,int l,int r,int ql,int qr)
{
if(ql>r||l>qr)return 0;
if(ql<=l&&r<=qr)return a[i].x;
int mid=(l+r)>>1;
return query(a[i].l,l,mid,ql,qr)+query(a[i].r,mid+1,r,ql,qr);
}
int main()
{
int Tes,i,j,n,m,u,l,r,d;
scanf("%d",&Tes);
while(Tes--){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&col[i]);
for(i=2;i<=n;i++){scanf("%d",&u);adde(u,i);}
dc=mxd=0;
dfs1(1);top[1]=1;dfs2(1);
for(i=1;i<=mxd;i++){
T[i]=T[i-1];
for(j=0;j<int(G[i].size());j++){
u=G[i][j];l=r=0;
it1=it2=S[col[u]].lower_bound(dfn[u]);
if(it1!=S[col[u]].begin())it1--,l=num[*it1];
edi=S[col[u]].end();
if(it2!=edi)r=num[*it2];
insert(T[i],1,dc,dfn[u],1,T[i-1]);
if(l)
insert(T[i],1,dc,dfn[LCA(u,l)],-1,T[i-1]);
if(r)
insert(T[i],1,dc,dfn[LCA(u,r)],-1,T[i-1]);
if(l&&r)
insert(T[i],1,dc,dfn[LCA(l,r)],1,T[i-1]);
S[col[u]].insert(dfn[u]);
}
}
int las=0;
for(i=1;i<=m;i++){
scanf("%d%d",&u,&d);
u^=las;d^=las;
int nd=min(mxd,d+dep[u]);
las=query(T[nd],1,dc,dfn[u],dfn[u]+siz[u]-1);
printf("%d\n",las);
}
cnt=0;tot=0;
for(i=1;i<=n;i++){
fir[i]=0;
S[col[i]].clear();
dfn[i]=num[i]=0;
siz[i]=fa[i]=son[i]=top[i]=dep[i]=0;
}
for(i=1;i<=mxd;i++)G[i].clear(),T[i]=0;
}
}
题意:一棵n个点的树,求选出任意两点,其距离为质数的概率
题解:点分治+FFT版题,子树去重一下即可
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 100005
int n;
int prime[N],tot;
bool pvis[N];
void shai()
{
int i,j;pvis[1]=1;
for(i=2;i<=n;i++){
if(!pvis[i])prime[++tot]=i;
for(j=1;j<=tot;j++){
int tmp=i*prime[j];
if(tmp>n)break;
pvis[tmp]=1;
if(i%prime[j]==0)break;
}
}
}
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int tmpsiz[N],nrt,all;
bool vis[N];
void findrt(int u,int ff)
{
tmpsiz[u]=1;int mx=0;
for(int v,p=fir[u];p;p=nxt[p]){
if((v=to[p])!=ff&&!vis[v]){
findrt(v,u);
tmpsiz[u]+=tmpsiz[v];
mx=max(mx,tmpsiz[v]);
}
}
mx=max(mx,all-tmpsiz[u]);
if(2*mx<=all)nrt=u;
}
int getrt(int u,int sz)
{
all=sz;nrt=-0x3f3f3f3f;
findrt(u,0);return nrt;
}
struct cp{
double r,i;
cp(){}
cp(double x,double y){r=x;i=y;}
cp operator + (const cp &t){return cp(r+t.r,i+t.i);}
cp operator - (const cp &t){return cp(r-t.r,i-t.i);}
cp operator * (const cp &t){return cp(r*t.r-i*t.i,r*t.i+i*t.r);}
}w,wn;
int rev[N];
const double PI=3.14159265358979323846264338;
void FFT(vector<cp> &a,int len,int flg)
{
int i,j,k;
for(i=0;i<len;i++)rev[i]=(rev[i>>1]>>1)|((i&1)*(len>>1));
for(i=0;i<len;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(i=1;i<len;i<<=1){
wn=cp(cos(PI/i),sin(PI/i));
if(flg==-1)wn.i=-wn.i;
for(j=0;j<len;j+=(i<<1)){
w=cp(1,0);
for(k=j;k<i+j;k++){
cp u=a[k],v=a[k+i]*w;
a[k]=u+v;
a[k+i]=u-v;
w=w*wn;
}
}
}
if(flg==-1)
for(i=0;i<len;i++)
a[i].r/=len;
}
vector<cp> a,c;
void pre(int u,int ff,int d)
{
tmpsiz[u]=1;
while(d>=(int)a.size())a.push_back(cp(0,0));
a[d].r++;
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]&&v!=ff){
pre(v,u,d+1);
tmpsiz[u]+=tmpsiz[v];
}
}
}
long long ans;
void update(vector<cp> &a,int flg)
{
c.clear();int len=1;
while(len<2*(int)a.size())len<<=1;
while((int)a.size()<len)a.push_back(cp(0,0));
while((int)c.size()<len)c.push_back(cp(0,0));
FFT(a,len,1);
for(int i=0;i<len;i++)c[i]=a[i]*a[i];
FFT(c,len,-1);
for(int i=2;i<len;i++)if(!pvis[i])
ans=ans+1ll*flg*(long long)(c[i].r+0.5);
}
void DFZ(int u)
{
vis[u]=1;
a.clear();pre(u,0,0);
update(a,1);
for(int v,p=fir[u];p;p=nxt[p])
if(!vis[v=to[p]]){
a.clear();pre(v,u,1);
update(a,-1);
}
for(int v,p=fir[u];p;p=nxt[p])
if(!vis[v=to[p]])DFZ(getrt(v,tmpsiz[v]));
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("WA.out","w",stdout);
int i,u,v;
n=gi();shai();
for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
DFZ(getrt(1,n));
ans/=2;
printf("%.6f\n",(double)ans/((long long)n*(n-1)/2));
}
题意:给一个n个点的树与一个长度为n的排列a,每次修改交换排列上相邻的两个数, 每次询问给出l,r,v,求al~ar每个点到v的距离和
题解:可持久化点分树
像之前那道题一样做的话时间空间都是无法承受的
考虑到如果可以维护出a1~ai的点分树(每个点分中心维护a的前缀点集到它的距离和以及它点分区域包含点的个数)
那么我们可以直接差分得到答案了
于是,可持久化点分树就诞生了,每次加入一个点就对应修改点分树上的一条链
但是事与愿违,我们对于每个点分中心还要保存它儿子的信息来去重,而如果是菊花图就会把可持久化点分树卡到n^2
所以我们还需要把原树进行三度化
代码:(细节过多引起极度不适)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 400005
int n,m,P[N];
int fir[N],to[2*N],nxt[2*N],cd[2*N],cnt,du[N];
void adde(int a,int b,int c)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cd[cnt]=c;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cd[cnt]=c;
du[a]++;du[b]++;
}
struct node{
int u,v,w;
node(){}
node(int x,int y,int z){u=x;v=y;w=z;}
}e[N];int ecnt;
void sandu(int u,int ff)
{
int pre=u;
for(int v,p=fir[u];p;p=nxt[p]){
v=to[p];if(v==ff)continue;
if(du[u]-bool(ff)>=3){
e[++ecnt]=node(pre,++n,0);
e[++ecnt]=node(n,v,cd[p]);
pre=n;
}
else e[++ecnt]=node(u,v,cd[p]);
sandu(v,u);
}
}
bool vis[N];
int tmpsiz[N],all,nrt;
void findrt(int u,int ff)
{
tmpsiz[u]=1;int mx=0;
for(int v,p=fir[u];p;p=nxt[p]){
if((v=to[p])!=ff&&!vis[v]){
findrt(v,u);
tmpsiz[u]+=tmpsiz[v];
mx=max(mx,tmpsiz[v]);
}
}
mx=max(mx,all-tmpsiz[u]);
if(2*mx<=all)nrt=u;
}
int getrt(int u,int sz)
{
all=sz;nrt=-0x3f3f3f3f;
findrt(u,0);return nrt;
}
#define LOG 20
#define LL long long
LL dis[LOG][N];int from[LOG][N],dep[N];
int id[N*LOG],ch[N*LOG][4],T[N],tot;
LL sum[N*LOG],fsum[N*LOG],con[N*LOG];
void pre(int u,int ff,int d)
{
tmpsiz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]&&v!=ff){
dis[d][v]=dis[d][u]+1ll*cd[p];
pre(v,u,d);
tmpsiz[u]+=tmpsiz[v];
}
}
}
void DFZ(int u,int d)
{
dep[u]=d;id[u]=u;
vis[u]=1;pre(u,0,d);
for(int v,p=fir[u],so=0;p;p=nxt[p]){
if(!vis[v=to[p]]){
v=getrt(v,tmpsiz[v]);
ch[u][so]=v;from[d][v]=so;so++;
for(int i=0;i<d;i++)
from[i][v]=from[i][u];
DFZ(v,d+1);
}
}
}
void insert(int &u,int x)
{
id[++tot]=id[u];sum[tot]=sum[u];fsum[tot]=fsum[u];con[tot]=con[u];
for(int i=0;i<=2;i++)ch[tot][i]=ch[u][i];u=tot;
int d=dep[id[u]];
sum[u]+=dis[d][x];con[u]++;
if(d)fsum[u]+=dis[d-1][x];
if(id[u]==x)return;
insert(ch[u][from[d][x]],x);
}
LL query(int u,int x)
{
if(!u)return 0ll;
LL ret=sum[u]+con[u]*dis[0][x];
for(int i=1;i<=dep[x];i++){
u=ch[u][from[i-1][x]];
ret+=sum[u]+con[u]*dis[i][x];
ret-=fsum[u]+con[u]*dis[i-1][x];
}
return ret;
}
int main()
{
int u,v,w,i,tn,op,l,r,x;
tn=n=gi();m=gi();
for(i=1;i<=n;i++)P[i]=gi();
for(i=1;i<n;i++){u=gi();v=gi();w=gi();adde(u,v,w);}
sandu(1,0);
cnt=0;memset(fir,0,sizeof(fir));
for(i=1;i<=ecnt;i++)adde(e[i].u,e[i].v,e[i].w);
DFZ(T[0]=getrt(1,n),0);tot=n;
for(i=1;i<=tn;i++)
insert(T[i]=T[i-1],P[i]);
LL ans=0;
for(i=1;i<=m;i++){
op=gi();
if(op==1){
l=gi();r=gi();x=gi();
l^=ans;r^=ans;x^=ans;
ans=query(T[r],x)-query(T[l-1],x);
printf("%lld\n",ans);
ans=ans%(1<<30);
}
else{
x=gi();x^=ans;
swap(P[x],P[x+1]);
insert(T[x]=T[x-1],P[x]);
}
}
}
[JZOJ5520]Every one will meet some difficult
https://blog.csdn.net/hzj1054689699/article/details/85857283
考虑组合意义,利用生成函数将式子化为卷积形式,再暴力卷积+快速幂即可
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1005
#define LL long long
const int mod=1000109107;
int lim;
struct node{
int a[N];
node(){memset(a,0,sizeof(a));}
node operator * (const node &t)const{
node c;
for(int i=0;i<lim;i++)
for(int j=0;i+j<lim;j++)
c.a[i+j]=(c.a[i+j]+1ll*a[i]*t.a[j])%mod;
return c;
}
node operator ^ (LL y)const{
node ret,x=*this;ret.a[0]=1;
while(y){
if(y&1)ret=ret*x;
y>>=1;x=x*x;
}
return ret;
}
}a,b;
int main()
{
int i,j;LL s,t,n,m;
scanf("%lld%lld%lld%lld",&s,&t,&n,&m);
lim=m-n+2;
a.a[0]=a.a[1]=b.a[0]=b.a[1]=1;
a=a^t;b=b^(s-n*t);
for(i=0;i<lim;i++)a.a[i]=a.a[i+1];
a=a^n;a=a*b;
printf("%d",a.a[m-n]);
}
20200518鱼贯而入
题意:给出n个数,求一个hash表的大小len,使这n个数撞hash的次数最多,输出撞hash的次数。
(这里的hash表是:如果y=x%len撞了hash,那么就把y放到y+1位置(如果继续撞就再+1))
n<=200
题解:乱搞题
先暴力验证一下小范围的len,再n个数任选两个做差,验证一下差的所有因子
这里的差可能比较大,PR一下即可
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
#define N 205
#define LL long long
int nn,ans;LL a[N];
map<LL,LL> h;
void add_fish(int &cnt,LL x,LL len)
{
LL y=x%len;
while(h.count(y)&&h[y]!=x)y=(y+1)%len,cnt++;
h[y]=x;
}
//#include<ctime>
//double sum;
void solve(LL len)
{
//double c1=clock();
h.clear();int cnt=0,i;
for(i=1;i<=nn;i++) add_fish(cnt,a[i],len);
//if(cnt>ans)printf("%lld\n",len);
ans=max(cnt,ans);
//sum+=clock()-c1;
}
inline LL mul(LL a,LL b,LL p){return (a*b-(LL)((long double)a*b/p)*p+p)%p;}
LL ksm(LL x,LL y,LL mod)
{
LL ans=1;
while(y){
if(y&1) ans=mul(ans,x,mod);
y>>=1;x=mul(x,x,mod);
}
return ans;
}
bool MR(LL n)
{
if(n==2) return 1;
if(n<2||!(n&1)) return 0;
LL m=n-1;int i,j,k=0;
while(!(m&1)){k++;m>>=1;}
for(i=0;i<10;i++){
LL a=rand()%(n-1)+1,x=ksm(a,m,n),y=0;
for(j=0;j<k;j++){
y=mul(x,x,n);
if(y==1&&x!=1&&x!=n-1) return 0;
x=y;
}
if(y!=1) return 0;
}
return 1;
}
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
LL Rho(LL n,LL c)
{
LL x=rand()%n+1,y=x,q=1,d=1,k=1;
while(1){
for(int i=1;i<=k;i++){
x=(mul(x,x,n)+c)%n;
q=mul(q,abs(x-y),n);
if(!(i&127)&&(d=gcd(q,n))>1) break;
}
if(d>1||(d=gcd(q,n))>1)
return d==n?Rho(n,c+1):d;
k<<=1;q=1;y=x;
}
}
//int con;
void Pollar(LL n)
{
if(n==1) return;
if(MR(n)){if(n>2000)solve(n);return;}
LL p=Rho(n,3);
while(n%p==0) n/=p;
Pollar(p);Pollar(n);
}
int main()
{
//freopen("3.in","r",stdin);
//double c1=clock();
srand(0);
int tp,i,j;
scanf("%d%d",&tp,&nn);
for(i=1;i<=nn;i++)scanf("%lld",&a[i]);
for(i=nn;i<=2000;i++)solve(i);
sort(a+1,a+nn+1);
for(i=2;i<=nn;i++){
int tt=min(i-1,10);
while(tt--){
j=rand()%(i-1)+1;
Pollar(a[i]-a[j]);
}
}
//printf("%d\n",con);
//printf("%.3f\n",sum/1000);
printf("%d\n",ans);
//printf("%.3fs\n",(clock()-c1)/1000);
}
小P的画
论文题
过于毒瘤,题解咕咕咕了
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N (1<<15)+1
#define LL long long
const int mod=998244353;
int con[N],lg[N],pw3[16];LL pw2[61];
int w[N],g[N],st[N],f[14348907];bool pd[N];
LL a[16];int rk[16],id[16];
bool cmp(int x,int y){return a[x]<a[y];}
int cnt;LL ta[N];int dp[2][2];
int solve(LL C)
{
if(C>ta[cnt]&&(C^ta[cnt])>ta[cnt])return 0;
if(ta[cnt]==0)return 1;
LL X=1ll<<(upper_bound(pw2,pw2+60,ta[cnt])-pw2-1);
dp[0][0]=dp[0][1]=dp[1][0]=dp[1][1]=0;
int now=0;
dp[now][0]=1;
for(int i=1;i<cnt;i++){
dp[now^1][0]=dp[now^1][1]=0;
if(ta[i]>=X){
dp[now^1][0]=(1ll*X%mod*dp[now][0]+1ll*(ta[i]-X+1)%mod*dp[now][1])%mod;
dp[now^1][1]=(1ll*X%mod*dp[now][1]+1ll*(ta[i]-X+1)%mod*dp[now][0])%mod;
}
else{
dp[now^1][0]=1ll*(ta[i]+1)%mod*dp[now][0]%mod;
dp[now^1][1]=1ll*(ta[i]+1)%mod*dp[now][1]%mod;
}
now^=1;
}
int ret;
if(C>=X)ret=dp[now][1];
else ret=dp[now][0];
ta[cnt]^=X;C^=X;
for(int i=cnt;i>1;i--){
if(ta[i]<ta[i-1])swap(ta[i],ta[i-1]);
else break;
}
return (ret+solve(C))%mod;
}
bool e[16][16];
int main()
{
int n,m,all,al3,i,s,t,u,v;LL C;
scanf("%d%d%lld",&n,&m,&C);
all=1<<n;
for(i=0;i<n;i++)scanf("%lld",&a[i]),id[i]=i;
sort(id,id+n,cmp);sort(a,a+n);
for(i=0;i<n;i++)rk[id[i]]=i;
lg[0]=-1;
for(s=1;s<all;s++){
lg[s]=lg[s>>1]+1;
con[s]=con[s>>1]+(s&1);
}
pw2[0]=1;pw3[0]=1;
for(i=1;i<=60;i++)pw2[i]=2ll*pw2[i-1];
for(i=1;i<=n;i++)pw3[i]=3*pw3[i-1];
al3=pw3[n];
for(i=1;i<=m;i++){
scanf("%d%d",&u,&v);
if(u>v)swap(u,v);e[u][v]=1;
u=rk[u-1];v=rk[v-1];
pd[(1<<u)|(1<<v)]=1;
}
for(i=0;i<n;i++)for(s=0;s<all;s++)
if(s&(1<<i))pd[s]|=pd[s^(1<<i)];
for(s=1;s<all;s++){
w[s]=!pd[s];// \sum_{i=0}^{Ecnt[s]} C(Ecnt[s],i)*(-1)^i=0^(Ecnt[s])
for(t=(s-1)&s;t;t=(t-1)&s)if(t&(s&-s))
w[s]=(w[s]-w[t]*(!pd[s^t]))%mod;
}
for(s=1;s<all;s++)if(!(con[s]&1))w[s]=1ll*(a[lg[s&-s]]+1)%mod*w[s]%mod;
for(s=1;s<all;s++){
cnt=0;
for(i=0;i<n;i++)if(s&(1<<i))
ta[++cnt]=a[i];
g[s]=solve(C);
}
for(s=1;s<all;s++)st[s]=st[s>>1]*3+(s&1);
for(s=1;s<all;s++)if(con[s]&1)st[s]+=pw3[lg[s&-s]];
f[0]=1;if(C==0)g[0]=1;
int ans=0;
for(s=0;s<al3;s++)if(f[s]){
int flg=0,s0=0;
for(i=0;i<n;i++)if(s/pw3[i]%3==0)s0|=1<<i;
if(s0){
flg=(s0&-s0);s0-=flg;
for(t=s0;;t=(t-1)&s0){
int tmp=s+st[t+flg];
f[tmp]=(1ll*f[tmp]+1ll*f[s]*w[t+flg])%mod;
if(!t)break;
}
}
else{
int s2=0;
for(i=0;i<n;i++)if(s/pw3[i]%3==2)s2|=1<<i;
ans=(1ll*ans+1ll*f[s]*g[s2])%mod;
}
}
printf("%d\n",(ans+mod)%mod);
}
20200521river
n<=10^9,m<=10^6
题解:贪心
先求出每种天气下到达下一个地点的最少天数,我们一定是每次都选择最少天数的方法去走
于是我们可以建出一个有向图,且带有一个环,跟Pollard_Rho中的Rho很像的
先算一下环外链的权值,再算一下环的权值
我们如果走完了环外了链,就会到环上转圈圈
环权值乘上我们转的圈数就是我们转圈的代价,如果还没有到达目的地,就再手动把剩下的几步加上
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 1000005
#define LL long long
const LL INF=0x3f3f3f3f3f3f3f3fll;
int a[N],dfn[N],cnt,fir,len;LL g[N];bool vis[N];
int main()
{
int n,m,i,j;
LL pre=INF,suf=INF,ans=0,sum=0;
n=gi();m=gi();
for(i=0;i<m;i++)a[i]=gi(),g[i]=INF;
for(i=1;i<m;i++)pre=min(pre,1ll*a[i-1]+i-1),g[i]=min(g[i],pre+1ll*m-1ll*i);
for(i=m-1;i>=0;i--)suf=min(suf,1ll*a[i]+i),g[i]=min(g[i],suf-1ll*i);
for(i=0;!vis[i];i=(i+g[i])%m)
vis[i]=1,dfn[++cnt]=i;
fir=i;
for(i=1;dfn[i]!=fir&&i<=cnt;i++)
ans+=g[dfn[i]],n--;
if(n>0){
for(j=i;j<=cnt;j++)sum+=g[dfn[j]];
ans+=n/(cnt-i+1)*sum;n%=(cnt-i+1);
for(j=i;n;j++,n--)ans+=g[dfn[j]];
}
printf("%lld\n",ans);
}
20200521cac
题解:圆方树+树状数组,把方点与圆点的贡献分开来计算,方点用树状数组统计,圆点就直接加权值
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 600005
#define M 1000005
int fir[N],nxt[2*N],to[2*N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int dfn[N],low[N],stk[N],tp,dc,pbccnt;
vector<int> pbc[N];
void dfs(int u,int fa)
{
dfn[u]=low[u]=++dc;
stk[tp++]=u;
int v,p;
for(p=fir[u];p;p=nxt[p]){
v=to[p];
if(!dfn[v]){
dfs(v,u);low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
pbccnt++;
while(tp>0){
pbc[pbccnt].push_back(stk[--tp]);
if(stk[tp]==v)
break;
}
pbc[pbccnt].push_back(u);
}
}
else if(v!=fa) low[u]=min(low[u],dfn[v]);
}
}
int fa[N],dep[N],son[N],siz[N],top[N];
void dfs1(int u)
{
dep[u]=dep[fa[u]]+1;
int v,p;siz[u]=1;
for(p=fir[u];p;p=nxt[p]){
v=to[p];
if(v!=fa[u]){
fa[v]=u;dfs1(v);siz[u]+=siz[v];
if(siz[son[u]]<siz[v])
son[u]=v;
}
}
}
const int mod=998244353;
int pos[N],ddfn;
void dfs2(int u)
{
pos[u]=++ddfn;
if(son[u]) top[son[u]]=top[u],dfs2(son[u]);
int v,p;
for(p=fir[u];p;p=nxt[p]){
v=to[p];
if(v!=son[u]&&v!=fa[u])
top[v]=v,dfs2(v);
}
}
int LCA(int x,int y)
{
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int tra[N];
void update(int x,int k)
{
if(!x)return;
while(x<=600000){
tra[x]=(1ll*tra[x]+1ll*mod+1ll*k)%mod;
x+=(x&-x);
}
}
int getsum(int x)
{
int ret=0;
while(x){
ret=(ret+tra[x])%mod;
x-=(x&-x);
}
return ret;
}
int val[N];
int main()
{
int n,m,Q,i,j,k,u,v,op;
n=gi();m=gi();Q=gi();
for(i=1;i<=m;i++){
u=gi();v=gi();
adde(u,v);
}
for(i=1;i<=n;i++)
if(!dfn[i])dfs(i,0);
memset(fir,0,sizeof(fir));cnt=0;
for(i=1;i<=pbccnt;i++)
for(j=0,k=pbc[i].size();j<k;j++)
adde(n+i,pbc[i][j]);
dfs1(1);top[1]=1;dfs2(1);
for(i=1;i<=Q;i++){
op=gi();
if(!op){
u=gi();v=gi();k=gi();
update(pos[fa[u]],k);update(pos[fa[v]],k);
int lca=LCA(u,v);
if(lca>n){
update(pos[lca],-k);(val[fa[lca]]+=k)%=mod;
lca=fa[lca],update(pos[fa[lca]],-k);
}
else{
update(pos[lca],-2*k%mod);
(val[lca]+=k)%=mod;
}
}
else{
u=gi();
int ans=val[u];
if(fa[u])u=fa[u],ans=(1ll*ans+1ll*getsum(pos[u]+siz[u]-1)+mod-getsum(pos[u]-1))%mod;
printf("%d\n",ans);
}
}
}
20200522A
题解:组合数——二项式定理——生成函数——等比数列求和
参考:Freopen大佬的博客
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000105
int n,m;
int pw[100],C[N],inv[N];
void exgcd(int a,int b,int &x,int &y)
{
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x);
y-=(a/b)*x;
}
int cmod,cans;
void CRT(int ans,int mod)
{
int t1,t2;
exgcd(cmod,mod,t1,t2);
int A=cmod*mod;
cans=(1ll*cmod*t1%A*(ans-cans)+cans)%A;
cmod=A;
}
int solve(int p,int e,int mod)// calculate the answer mod p^e
{
int c=1,cnte=0,i,tmp;
inv[0]=inv[1]=1;
for(i=2;i<max(3,min(mod,m+2));i++)
exgcd(i,mod,inv[i],tmp);
for(i=1;i<=m+1;i++){
int x=n-i+1,y=i;
if(x)for(;x%p==0;x/=p,cnte++);
for(;y%p==0;y/=p,cnte--);
c=1ll*c*x%mod*inv[y%mod]%mod;
if(cnte>=e)C[i]=0;
else C[i]=1ll*c*pw[cnte]%mod;
}
int b=0,ret=0;
for(i=0;i<=m;i++){
b=1ll*(C[i+1]-b)*inv[2]%mod;
if(!(i&1)) ret=(ret+b)%mod;
}
return ret;
}
int solve2(int e,int mod)//answer mod 2^e
{
int c=1,cnte=0,i,tmp;
inv[0]=inv[1]=1;
for(i=2;i<=m+2+e;i++)
exgcd(i,mod,inv[i],tmp);
for(i=1;i<=m+2+e;i++){
int x=n-i+1,y=i;
if(x)for(;!(x&1);x>>=1)cnte++;
for(;!(y&1);y>>=1)cnte--;
c=1ll*c*x%mod*inv[y%mod]%mod;
if(cnte>=e)C[i]=0;
else C[i]=((1ll*c)<<cnte)%mod;
}
int b=0;
for(i=m+1+e;i>=m+2;i--)
b=(1ll*C[i]-2ll*b)%mod;
int ret=b;
for(i=m-1;i>=0;i--){
b=(1ll*C[i+2]-2ll*b)%mod;
if(!(i&1)) ret=(ret+b)%mod;
}
return ret;
}
int main()
{
int i,mod;
scanf("%d%d%d",&n,&m,&mod);
n+=2;if(n&1)n--;if(m&1)m--;
int cnte2=0;
for(;!(mod&1);mod>>=1,cnte2++);
//1000000000 1000000 536870912
cans=0;cmod=pw[0]=1;
for(i=3;i*i<=mod;i++){
if(mod%i==0){
int cnt=0;
while(mod%i==0){
mod/=i;cnt++;
pw[cnt]=pw[cnt-1]*i;
}
CRT(solve(i,cnt,pw[cnt]),pw[cnt]);
}
}
if(mod>1)CRT(solve(mod,1,mod),mod);
mod=1<<cnte2;
CRT(solve2(cnte2,mod),mod);
printf("%d\n",(cans%cmod+cmod)%cmod);
}