A.CCA的词典
没啥好讲的,map记录下出没出现过,然后每次查询枚举下可能的来源,叠加贡献即可。注意特判aa这种情况
map<string,int>cnt; int main(){ int n; cin>>n; for(int i=0;i<n;++i){ string t; cin>>t; cnt[t]++; } int q; read(q); while(q--){ string s; cin>>s; int ans=cnt[s]; if(s.size()==2&&s[0]!=s[1]){ swap(s[0],s[1]); ans+=cnt[s]; } cout<<ans<<endl; } return 0; }
B.CCA的搬运
差点死在这题。。
想到了最优的顺序是按照要拿的顺序堆在一起,但是没注意到一个东西可以拿几次。。
按照第一次出现的顺序堆起来,之后模拟即可。
const int maxn=2e3+10; struct node{ int val,id; }st[maxn]; int top; int w[maxn]; int vis[maxn]; int pi[maxn]; int n,m; int main(){ //freopen("in.txt","r",stdin); read(n),read(m); for(int i=1;i<=n;++i)read(w[i]); for(int i=1;i<=m;++i){ int x; read(x); pi[i]=x; if(!vis[x]){ st[++top]={w[x],x}; } vis[x]=1; } int ans=0; for(int i=1;i<=m;++i){ for(int j=1;j<=top;++j){ if(st[j].id==pi[i]){ node tmp=st[j]; for(int k=j;k>=2;--k){ st[k]=st[k-1]; } st[1]=tmp; break; } else{ ans+=st[j].val; } } } cout<<ans<<endl; return 0; }
C.CCA的子树
还是比较好想的树形dp。
题目要求选择的两个子树根节点不能有祖孙关系,其实也就是说,他们得是一个节点下,任意两个儿子所在子树的子树(有点绕),也就是说,他们的lca不能是他们其中的一个。若分别取枚举两个子树的根节点则需要的时间,所以考虑在他们的lca处合并。对于一个lca,我们向下找要求的子树即可(qaq真的不知道怎么表达)。具体看代码:
#define int ll const int maxn=2e5+10; struct Edge{ int to,next; }e[maxn<<1]; int head[maxn],cnt; void add(int x,int y){ e[cnt].to=y; e[cnt].next=head[x]; head[x]=cnt++; } int w[maxn]; int sum_of_son[maxn]; int mx_of_son[maxn]; int n; void dfs(int u,int fa){ sum_of_son[u]=w[u]; for(int i=head[u];~i;i=e[i].next){ int v=e[i].to; if(v==fa)continue; dfs(v,u); sum_of_son[u]+=sum_of_son[v]; } } int ans=-inf; void solve(int u,int fa){ vector<int>tmp; for(int i=head[u];~i;i=e[i].next){ int v=e[i].to; if(v==fa)continue; solve(v,u); tmp.push_back(mx_of_son[v]); } sort(tmp.begin(),tmp.end(),greater<int>()); mx_of_son[u]=sum_of_son[u]; if(tmp.size()<1)return ; mx_of_son[u]=max(mx_of_son[u],tmp[0]); if(tmp.size()<2)return ; ans=max(ans,tmp[0]+tmp[1]); } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif //clock_t c1 = clock(); //=========================================================== read(n); memset(mx_of_son,-inf,sizeof(mx_of_son)); memset(head,-1,sizeof(head)); rep(i,1,n)read(w[i]); rep(i,2,n){ int u,v; read(u),read(v); add(u,v);add(v,u); } dfs(1,-1); solve(1,-1); //cout<<endl; if(ans==-inf){ puts("Error"); } else{ cout<<ans<<endl; } //=========================================================== //std::cerr << "Time:" << clock() - c1 << "ms" << std::endl; return 0; }
D.CCA的小球
这题本质解法是容斥原理。但个人感觉细节还是蛮多的Orz。
首先,正面算的话想不出来,所以考虑下容斥。
那么,正难则反,考虑n个球任意排列,方案数为n!.
题目要求不能有相邻球颜色相同,所以,我们需要减去不合理方案。根据容斥公式:
设至少有i组相邻颜色相同小球的方案数为f(i)
则,答案为
考虑:假设现在要求至少i组相邻小球颜色相同,则,考虑捆绑法,将这i组小球分别捆绑,则,原来n个小球就变成了n-i个小球全排列。同时,考虑我们上面的总情况没有去除两个相同颜色小球颠倒位置的情况,所以需要乘上,综上
最后,我们还是要去掉两个颜色相同小球颠倒的情况,所以,乘个的逆元即可。
另外,对于上面的式子,我们也可以用二项式反演处理:
设g(i)为恰好有i组相邻小球颜色相同,则:
(为什么要乘上组合数:每次可以选择不同颜色相邻)
根据反演公式:
以此代替容斥原理(不过感觉难点不在这吧,而是在如何求出)
#include <bits/stdc++.h> #define inf 0x3f3f3f3f #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define rep(i, a, n) for(int i = a; i <= n; ++ i) #define per(i, a, n) for(int i = n; i >= a; -- i) //#define ONLINE_JUDGE using namespace std; typedef long long ll; const int mod=1e9+7; template<typename T>void write(T x) { if(x<0) { putchar('-'); x=-x; } if(x>9) { write(x/10); } putchar(x%10+'0'); } template<typename T> void read(T &x) { x = 0;char ch = getchar();ll f = 1; while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();} while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f; } int gcd(int a,int b){return b==0?a:gcd(b,a%b);} int lcm(int a,int b){return a/gcd(a,b)*b;}; ll ksm(ll a,ll n){ ll ans=1; while(n){ if(n&1) ans=(ans*a)%mod; a=a*a%mod; n>>=1; } return ans%mod; } //============================================================== const int maxn=1e6+10; #define int ll int n; int a[maxn]; int f[maxn]; int pow2[maxn]; int finv[maxn]; int inv2[maxn]; unordered_map<int,int>cnt; void init(){ f[0]=1; rep(i,1,maxn-1)f[i]=f[i-1]*i%mod; finv[maxn-1]=ksm(f[maxn-1],mod-2); for(int i=maxn-2;i>=0;--i){ finv[i]=1ll*finv[i+1]*(i+1)%mod; } pow2[0]=1; for(int i=1;i<maxn;++i){ pow2[i]=1ll*pow2[i-1]*2%mod; } inv2[0]=1; ll inv22=ksm(2,mod-2); for(int i=1;i<maxn;++i){ inv2[i]=inv2[i-1]*inv22%mod; } } ll C(ll n,ll m){ if(m>n||m<0)return 0; //cerr<<n<<" "<<m<<endl; //cerr<<f[n]<<" "<<finv[n-m]<<" "<<finv[m]<<endl; return 1ll*f[n]*finv[n-m]%mod*finv[m]%mod; } int sig(int x){ return (x&1)?-1:1; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif //clock_t c1 = clock(); //=========================================================== init(); read(n); ll res=f[n]; for(int i=1;i<=n;++i){ read(a[i]); cnt[a[i]]++; } int cnt2=0; for(auto x:cnt)cnt2+=(x.second==2); for(int i=1;i<=cnt2;++i){ res=(res+sig(i)*C(cnt2,i)*f[n-i]%mod*pow2[i]%mod)%mod; res=(res%mod+mod)%mod; } cout<<res*inv2[cnt2]%mod<<endl; //=========================================================== //std::cerr << "Time:" << clock() - c1 << "ms" << std::endl; return 0; }