比賽地址ui
Orz Anson&Deadechospa
枚舉\(b,c\),這樣\(a,d\)的限制也就肯定了,二維數點便可。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=505; int n,a[N],sum[N][N];long long ans; int getsum(int x1,int x2,int y1,int y2){ return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]; } int main(){ n=gi(); for(int i=1;i<=n;++i)a[i]=gi(),++sum[i][a[i]]; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]; for(int i=2;i<=n;++i) for(int j=i+1;j<=n;++j) if(a[i]>a[j]) ans+=getsum(1,i-1,1,a[j]-1)*getsum(j+1,n,a[i]+1,n); printf("%lld\n",ans);return 0; }
好像被我強行水過去了?get
考慮一個區間的貢獻\(seed^{(l-1)n+r}\),能夠拆成\(seed^{(l-1)n}\times seed^r\),因此依然能夠考慮枚舉每個數做爲中位數,將大於它的視做\(1\)小於它的視做\(-1\),找到全部包含這個數且和爲\(0\)或\(\pm 1\)的區間(由於要考慮中位數有兩個的狀況),將左端點與右端點的權值相乘便可獲得答案。string
#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; } #define ll long long const int N=10005; const int mod=1e9+7; int n,seed,a[N],p[N],P[N],tl[N<<1],tr[N<<1],ans; inline void add(int &x,int y){x+=y;x>=mod?x-=mod:x;} int main(){ n=gi();seed=gi(); for(int i=1;i<=n;++i)a[i]=gi(); p[0]=1;p[1]=seed; for(int i=2;i<=n;++i)p[i]=1ll*p[i-1]*p[1]%mod; P[0]=1;P[1]=p[n]; for(int i=2;i<=n;++i)P[i]=1ll*P[i-1]*P[1]%mod; for(int i=1;i<=n;++i){ memset(tl,0,sizeof(tl)); memset(tr,0,sizeof(tr)); int res=0; for(int j=i-1,s=0;j>=1;--j)s+=(a[j]>a[i]?1:-1),add(tl[s+N],P[j-1]); for(int j=i+1,s=0;j<=n;++j)s+=(a[j]>a[i]?1:-1),add(tr[s+N],p[j]); for(int x=min(i-1,n-i)+1,j=-x;j<=x;++j) res=(res+((ll)tl[j+N]+tl[j+N]+tl[j-1+N]+tl[j+1+N])*tr[-j+N])%mod; res=(res+((ll)tr[N]+tr[N]+tr[1+N]+tr[-1+N])*P[i-1])%mod; res=(res+((ll)tl[N]+tl[N]+tl[1+N]+tl[-1+N])*p[i])%mod; res=(res+(ll)P[i-1]*p[i]*2)%mod; ans=(ans+(ll)res*a[i])%mod; } printf("%d\n",ans); return 0; }
考慮強制一個點最後一個刪,將其做爲根節點,這樣刪點的順序就必定是從葉子到根,於是能夠作一個簡單\(dp\)求出方案數,合併子樹時方案數乘上組合數便可。it
如今要求以每一個點爲根(每一個點最後一個刪)的答案。直接換根\(dp\)便可。io
#include<cstdio> #include<algorithm> #include<vector> 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=1e5+5; const int mod=998244353; int n,inv[N],jc[N],jcn[N],sz[N],dp[N],ans;vector<int>E[N]; 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 C(int n,int m){return 1ll*jc[n]*jcn[m]%mod*jcn[n-m]%mod;} void work(int u,int v){ sz[u]+=sz[v],dp[u]=1ll*dp[u]*dp[v]%mod*C(sz[u]-1,sz[v])%mod; } void rework(int u,int v){ dp[u]=1ll*dp[u]*fastpow(1ll*dp[v]*C(sz[u]-1,sz[v])%mod,mod-2)%mod,sz[u]-=sz[v]; } void dfs1(int u,int f){ sz[u]=dp[u]=1; for(int v:E[u])if(v!=f)dfs1(v,u),work(u,v); } void dfs2(int u,int f){ ans=(ans+dp[u])%mod; for(int v:E[u])if(v!=f)rework(u,v),work(v,u),dfs2(v,u),rework(v,u),work(u,v); } int main(){ n=gi();inv[1]=jc[0]=jcn[0]=1; for(int i=2;i<=n;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=1ll*jcn[i-1]*inv[i]%mod; for(int i=1;i<n;++i){ int u=gi(),v=gi(); E[u].push_back(v);E[v].push_back(u); } dfs1(1,0);dfs2(1,0);printf("%d\n",ans);return 0; }
不難發現答案式爲\(\sum_{i=l}^r\binom{i+n-1}{n-1}\binom{s-i+m}{m}\)。ast
這是一個\(n+m\)次多項式然而沒辦法在\(O(n+m)\)的時間裏算出前\(n+m\)項。class
考慮其組合意義。至關於從\((0,0)\)點出發走到\((s,n+m)\),每步能夠向上走或是向右走,且保證第\(n\)步向上走時橫座標在\([l,r]\)內的方案數。test
這個等價於求第\(l\)步向右走時縱座標在\([0,n-1]\)的方案數減去第\(r+1\)步向右走時縱座標在\([0,n-1]\)的方案數,而這些是能夠作到\(O(n+m)\)計算的。
#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=2e7+5; const int mod=998244353; int n,m,s,l,r,inv[N],f[N],g[N]; int cal(int p){ if(p>s)return 0;int res=0; //\sum_{i=0}^{n-1}\binom{i+p-1}{i}\binom{n+m-1+s-p}{n+m-i} for(int i=f[0]=g[0]=1;i<=n+m;++i){ f[i]=1ll*f[i-1]*(s-p+i)%mod*inv[i]%mod; g[i]=1ll*g[i-1]*(p-1+i)%mod*inv[i]%mod; } for(int i=0;i<n;++i)res=(res+1ll*f[n+m-i]*g[i])%mod; return res; } int main(){ n=gi(),m=gi(),s=gi(),l=gi(),r=gi();inv[1]=1; for(int i=2;i<N;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; printf("%d\n",(cal(l)-cal(r+1)+mod)%mod);return 0; }
不難發現題目要求的就是仙人掌上每一條路徑的長度之和。
考慮分別計算每一條邊出如今了多少條路徑中。對仙人掌建圓方樹,而後這個方案數就能夠用簡單換根\(dp\)求出。注意每通過一個方點(一個環)時方案數要乘\(2\)。
對於環邊,顯然環上的每一條邊的計算次數都是相同的,因而在\(dfs\)到方點的同時計算一下答案便可。
#include<cstdio> #include<algorithm> #include<vector> 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; } #define pi pair<int,int> #define mk make_pair #define fi first #define se second const int N=2e5+5; int n,tot,m,mod,fa[N],dep[N],dis[N],sum[N],mrk[N],sz[N],ans; vector<pi>G1[N],G2[N]; void link(int u,int v,int w,vector<pi>*G){ G[u].push_back(mk(v,w));G[v].push_back(mk(u,w)); } void dfs(int u,int f){ fa[u]=f;dep[u]=dep[f]+1; for(pi x:G1[u]) if(x.fi!=f) if(!dep[x.fi])dis[x.fi]=(dis[u]+x.se)%mod,dfs(x.fi,u); else if(dep[x.fi]>dep[u]){ sum[++tot]=(x.se+dis[x.first]-dis[u]+mod)%mod; for(int v=x.fi;v!=u;v=fa[v])link(tot,v,0,G2),mrk[v]=1; link(tot,u,0,G2); } } void work(int u,int v){ sz[u]=(sz[u]+(v<=n?1:2)*sz[v])%mod; } void undo(int u,int v){ sz[u]=(sz[u]-(v<=n?1:2)*sz[v]+mod+mod)%mod; } void dfs1(int u,int f){ sz[u]=(u<=n); for(pi x:G2[u])if(x.fi!=f)dfs1(x.fi,u),work(u,x.fi); } void dfs2(int u,int f){ if(u>n){ int res=0,tmp=0; for(pi x:G2[u])res=(res+1ll*tmp*sz[x.fi])%mod,tmp=(tmp+sz[x.fi])%mod; ans=(ans+1ll*res*sum[u])%mod; } for(pi x:G2[u]) if(x.fi!=f){ undo(u,x.fi);ans=(ans+1ll*sz[u]*sz[x.fi]%mod*x.se)%mod; work(x.fi,u);dfs2(x.fi,u);undo(x.fi,u);work(u,x.fi); } } int main(){ n=tot=gi();m=gi();mod=gi(); for(int i=1,x,y,z;i<=m;++i)x=gi(),y=gi(),z=gi(),link(x,y,z,G1); dfs(1,0); for(int i=2;i<=n;++i)if(!mrk[i])for(pi x:G1[i])if(x.fi==fa[i])link(i,fa[i],x.se,G2); dfs1(1,0);dfs2(1,0);printf("%d\n",ans);return 0; }
考慮一下存在二次剩餘的數\(x\)知足一些什麼樣的性質。
\(x^{\frac{p-1}{2}}\equiv1\mod p\)
而\(\mod p\)意義下恰有\(\frac{p-1}{2}\)個數存在二次剩餘,即恰有\(\frac{p-1}{2}\)個數知足上式。
根據某些代數高論咱們能夠獲得
\(x^{\frac{p-1}{2}}-1=\prod_{i\mbox{存在二次剩餘}}(x-i)\)
而咱們要求的東西即爲等式右邊的\(k\)次方與\(f(x)\)的\(\gcd\)。
能夠先求出\((x^{\frac{p-1}{2}}-1)^k\mod f(x)\)後再與\(f(x)\)取\(\gcd\)。複雜度\(O(n^2\log p)\)。
#include<cstdio> #include<algorithm> #include<vector> 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; } #define vi vector<int> const int mod=998244353; int inv(int a){ int res=1,b=mod-2; while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;} return res; } void print(vi a){ int n=a.size();printf("%d\n",n-1); for(int i=0;i<n;++i)printf("%d ",a[i]);puts(""); } vi mul(vi a,vi b){ int n=a.size(),m=b.size();vi c;c.resize(n+m-1); for(int i=0;i<n;++i) for(int j=0;j<m;++j) c[i+j]=(c[i+j]+1ll*a[i]*b[j])%mod; while(c.size()&&!c.back())c.pop_back();return c; } vi Mod(vi a,vi b){ int n=a.size(),m=b.size(); for(int i=n-1;i>=m-1;--i) if(a[i]){ int t=1ll*(mod-a[i])*inv(b[m-1])%mod; for(int j=0;j<m;++j)a[i-j]=(a[i-j]+1ll*b[m-1-j]*t)%mod; } while(a.size()&&!a.back())a.pop_back();return a; } vi fastpow(vi a,int b,vi c){ vi res;res.push_back(1); while(b){ if(b&1)res=Mod(mul(res,a),c); a=Mod(mul(a,a),c);b>>=1; } return res; } vi gcd(vi a,vi b){ if(!b.size())return a; return gcd(b,Mod(a,b)); } int main(){ int n=gi(),k=gi();vi f,g; for(int i=0;i<=n;++i)f.push_back(gi()); g.push_back(0);g.push_back(1); g=fastpow(g,mod-1>>1,f); if(!g.size())g.push_back(mod-1);else g[0]=(g[0]+mod-1)%mod; g=fastpow(g,k,f);g=gcd(f,g); int t=inv(g[g.size()-1]); for(int i=0;i<g.size();++i)g[i]=1ll*g[i]*t%mod; print(g);return 0; }