cf711D 成环的和不成环的要单独计算,环用双联通做的QAQ
/* 所有情况-成环的情况 */ #include<bits/stdc++.h> using namespace std; #define maxn 200005 #define mod 1000000007 #define ll long long struct Edge{int to,nxt;}edge[maxn<<1]; int n,head[maxn],tot; ll ans; void init(){ memset(head,-1,sizeof head); tot=0; } void addedge(int u,int v){ edge[tot].to=v;edge[tot].nxt=head[u];head[u]=tot++; } ll Pow(ll a,ll b){ ll res=1; while(b){ if(b%2)res=(res*a)%mod; b>>=1;a=a*a%mod; } return res; } int dfn[maxn],low[maxn],ind,bridge[maxn<<1]; void tarjan(int x,int in_edge){ dfn[x]=low[x]=++ind; for(int i=head[x];i!=-1;i=edge[i].nxt){ int y=edge[i].to; if(!dfn[y]){ tarjan(y,i); low[x]=min(low[x],low[y]); if(low[x]<low[y])bridge[i]=bridge[i^1]=1; } else if(i!=(in_edge^1)) low[x]=min(low[x],dfn[y]); } } int c[maxn],size[maxn],dcc; void dfs(int u){ c[u]=dcc;size[dcc]++; for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(c[v]||bridge[i])continue; dfs(v); } } int main(){ init(); ans=1; cin>>n; int v,cnt=0; for(int u=1;u<=n;u++){ cin>>v; addedge(u,v);addedge(v,u); } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i,0); //染色 for(int i=1;i<=n;i++) if(!c[i]){++dcc;dfs(i);} for(int i=1;i<=dcc;i++){ if(size[i]==1)cnt++; else ans=ans*(Pow(2,size[i])-2)%mod; } ans=ans*Pow(2,cnt)%mod; cout<<ans; return 0; }
cf340B 开始推公式了
/* 数组a组成的全排列 求差值之和 显然|ai-aj|出现的次数是(n-1)!次 那么答案就是sum|si-sj| / n 怎么求sum|si-sj|? 排个序,然后用前缀和求即可 8-4+2+1+2+3=4+8=12+12 */ #include<bits/stdc++.h> using namespace std; #define ll long long #define maxn 200005 ll tot,n,a[maxn],sum[maxn]; int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; sort(a+1,a+1+n); for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i]; for(int i=1;i<=n;i++){ tot+=sum[n]-sum[i]-a[i]*(n-i); tot+=a[i]*(i-1)-sum[i-1]; } //tot*=2; tot+=sum[n]; ll d=__gcd(n,tot); cout<<tot/d<<" "<<n/d<<endl; }
cf500D开始在树上推公式了,要注意最后用double。。ll不知道为什么会炸。。。
/* 找出所有路径然后求平均值 就是sum|(d(a,b)+d(b,c))*2|/C(n,3) 如何求sum, 边<u,v>的贡献次数是 cnt=C(size[u],2)*C(n-size[u]-1,1)+C(size[u],1)*C(n-size[u]-1,2) 其贡献是cnt*2*w */ #include<bits/stdc++.h> using namespace std; #define maxn 300005 #define ll long long struct Edge{int to,nxt;}edge[maxn<<1]; struct E{int u,v,w;}e[maxn]; int head[maxn],tot,n; double F; void init(){ memset(head,-1,sizeof head); tot=0; F=1; F=((double)n*(n-1)*(n-2))/6; } void addedge(int u,int v){ edge[tot].to=v;edge[tot].nxt=head[u];head[u]=tot++; } ll size[maxn]; void dfs(int u,int pre){ size[u]=1; for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(v==pre)continue; dfs(v,u);size[u]+=size[v]; } } ll cnt[maxn]; ll C(ll a,ll b){ if(a<b)return 0; if(b==1)return a; if(b==2)return (ll)a*(a-1)/2; } int main(){ cin>>n; init(); for(int i=1;i<n;i++){ cin>>e[i].u>>e[i].v>>e[i].w; addedge(e[i].u,e[i].v); addedge(e[i].v,e[i].u); } dfs(1,0); for(int i=1;i<n;i++){ int u=e[i].u,v=e[i].v; if(size[u]>size[v])swap(u,v); cnt[i]=(ll)C(size[u],2)*C(n-size[u],1)+(ll)C(size[u],1)*C(n-size[u],2); } double sum=0; for(int i=1;i<n;i++) sum+=(double)cnt[i]*2*e[i].w; //cout<<sum; int q; cin>>q; while(q--){ int id,w; cin>>id>>w; sum+=(double)(-(cnt[id]*2*e[id].w)+(cnt[id]*2*w)); e[id].w=w; printf("%lf ",sum/F); } }
cf150B 又是回文串拆分成奇偶串的题。。需要特判一些情况
/* 若k==n,那么就是可以组成的回文的个数 m^(n/2) k=1或k是偶数:全等 k是奇数:分奇偶串后全等即可 */ #include<bits/stdc++.h> using namespace std; #define mod 1000000007 #define ll long long ll n,m,k; ll Pow(ll a,ll b){ ll c=1; while(b){ if(b%2)c=c*a%mod; b>>=1;a=a*a%mod; } return c; } int main(){ cin>>n>>m>>k; if(k==1 || k>n){ cout<<(ll)Pow(m,n); return 0; } if(k<n){ if(k%2==0){ cout<<m; return 0; } cout<<(ll)m*m; return 0; } cout<<(ll)Pow(m,(n-1)/2+1); return 0; }
cf626D 好题!求c1+c2<c3的对数,由于数值较小,所以开个桶完美解决!
/* 显然有C(n,2)中选球组合 我们另第i中选球组合选出的求权值为(ai,bi) 那么选三次后要使得第二个人的总权值大于第一个人 即a1+a2+b3<b1+b2+a3 即(a1-b1)+(a2-b2)<(a3-b3) 我们另ai-bi=ci,变成求使c1+c2<c3成立的对数 如何求c1+c2<c3的对数,直接对c求是不可能了,复杂度似乎是o(n4)。。 、 可以开个桶+前缀和进行求解复杂度降到O(5000*5000) */ #include<bits/stdc++.h> using namespace std; #define maxn 5005 #define ll long long ll n,a[maxn],cnt[maxn],sum[maxn<<1]; int main(){ double F=1; cin>>n; F=n*(n-1)/2;F=F*F*F; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) cnt[abs(a[i]-a[j])]++; for(int i=0;i<=5000;i++){ if(cnt[i]==0)continue; for(int j=0;j<=5000;j++) sum[i+j]+=cnt[i]*cnt[j]; } for(int i=0;i<=5000;i++) sum[i]=sum[i-1]+sum[i]; double tot=0; for(int i=1;i<=5000;i++) tot+=(double)cnt[i]*sum[i-1]; printf("%.10lf",tot/F); return 0; }
cf272D 难点在于m不是个质数,不用逆元的话要用别的办法转化一下消去2即可
/* 统计每列的点数量 用乘法原理处理列间即可, 注意还要除以每列中相同的点的情况 因为一列中完全相同的点最多是2 2x=1(%m) */ #include<bits/stdc++.h> #include<map> using namespace std; #define maxn 200005 #define ll long long ll n,a[maxn],b[maxn]; map<ll,ll> s,cnt; map<ll,ll>::iterator it; ll m,ans,inv; ll f[maxn]; void init(){ f[0]=f[1]=1; for(int i=2;i<=100000;i++)f[i]=(ll)f[i-1]*i%m; } int main(){ cnt.clear();s.clear(); cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)cin>>b[i]; cin>>m;init(); for(int i=1;i<=n;i++){ if(a[i]==b[i])s[a[i]]++; cnt[a[i]]++,cnt[b[i]]++; } ll ans=1; for(it=cnt.begin();it!=cnt.end();it++){ ll c=(*it).first; ans=ans*f[cnt[c]-s[c]*2]%m; ll t=cnt[c]-s[c]*2; if(s[c]) for(int i=1;i<=s[c];i++){ t+=2;ans=ans*((t*(t-1))/2%m)%m; } } cout<<ans; }