A、怪盗-1412
思路:对于单次出现的元素比如'4','2',放在一起可以让序列1412的数量尽可能多,即被重复计算的次数多。
而对于出现两次的元素'1',分别设为考虑均值不等式,等于n,所以很明显,如果要最大,就应该使和尽可能平均分配。
以样例1为例,合理的排布应当是:111444444441112222222。
转述自某位大佬 传送门,大佬太强了。
Code:
#include<bits/stdc++.h> #define js ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define ll long long using namespace std; ll t,n,m,k; int main() { js; cin>>t; while(t--) { cin>>n>>m>>k; ll zuo=n/2; ll you=n-zuo; cout<<zuo*m*you*k<<endl; } }
B、Dis2
思路:每个点的答案ans[v]就是它父亲的边的数量size[u]-1,减一就除了自己那条边,其它的边u的另外一个端点都是和v距离为2的点。
Code:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+5; template <class T> inline void read(T &res) { char c; T flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } int head[maxn],Next[maxn<<1],to[maxn<<1],tot; void add(int x,int y) { to[++tot]=y; Next[tot]=head[x]; head[x]=tot; } ll ans[maxn],size[maxn]; void dfs(int u,int fa) { ans[fa]+=size[u]-1; for(int i=head[u];i;i=Next[i]) { int v=to[i]; if(v==fa) continue; ans[v]+=size[u]-1; dfs(v,u); } } int main() { int n,u,v; read(n); for(int i=2;i<=n;++i) { read(u),read(v); ++size[u],++size[v]; add(u,v),add(v,u); } dfs(1,0); for(int i=1;i<=n;++i) printf("%lld\n",ans[i]); }
C、序列卷积之和
思路:打表找规律,找出每个乘积的系数
打表代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+5; template <class T> inline void read(T &res) { char c; T flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } ll b[1000][1000]; int main() { int n; while(read(n),1) { memset(b,0,sizeof b); for(int l=1;l<=n;++l) for(int r=l;r<=n;++r) for(int i=l;i<=r;++i) for(int j=i;j<=r;++j) ++b[i][j]; for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) if(b[i][j]) { printf("b[%d][%d]=%lld\n",i,j,b[i][j]); } } }
以n==5为例进行化简可得:
= =
== = =====
= = ============
===== ====================
等于号是为了对齐的,很明显是前缀和(后缀和也行),这么明显的规律我就不写方程了。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+5,mod=1e9+7; template <class T> inline void read(T &res) { char c; T flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } ll a[maxn]; ll b[maxn]; int main() { int n; read(n); for (int i = 1; i <= n; ++i) read(a[i]); for (int i = n; i ; --i) b[i] = (b[i + 1] + a[i] * (n - i + 1) % mod) % mod; ll ans = 0; for (int i = 1; i <= n; ++i) (ans += a[i] * i % mod * b[i] % mod) %= mod; printf("%lld\n",ans); return 0; }
D、宝石装箱
知识点:dp、滚动数组、容斥原理
思路:反向递推,不直接求有多少符合的方案,而是先求出不符合要求的方案数,然后用总的方案数去它就是最终结果。摘自大佬的题解神崎兰子。
1.对应的二维dp是,表示前k个可能放错宝石的箱子(可能有些箱子没有限制,也就是不可能会放错,就不用考虑它)至少放错了i个的方案数。
2.很显然,对应的一维就是,表示有多少个宝石不能放到第k个箱子里,乘表示第i个宝箱放错了宝石。
3.求完了这些之后,我们可以用 减去1个位置不合法的方案数,那么2个位置不合法的情况被多减了一次,需要加回来,3个位置不合法的情况又被加多了,再减掉。。以此类推,最终求出来的就是合法方案的总数(就是奇减偶加,常见的容斥系数)。
4.因为2个位置不合法(比如a和b)的情况在a不合法和b不合法的统计中都被减了一次,相当于被减多了。
Code:
#include<bits/stdc++.h> #define js ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) using namespace std; #define ll long long const int mod = 998244353; ll jc[8005]; ll a[8005],t[8005],b[8005],dp[8005]; int main(){ ll n,i,j=0,k; cin>>n; ll tt=1; jc[0]=1; for(i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod; for(i=0;i<n;i++)cin>>a[i],t[a[i]]++; int jud=-1,temp=n; for(i=1;i<=n;i++){ if(t[i])b[++j]=t[i]; } ll res=jc[n]; dp[0]=1; for(i=1;i<=j;i++) for(k=i;k;--k){ dp[k]=(dp[k]+dp[k-1]*b[i])%mod; } for(i=1;i<=j;++i) res=(res+jud*jc[n-i]*dp[i])%mod,jud*=-1; cout<<(res+mod)%mod; }