2023河南icpc省赛5/13
A
考虑固定左端点,右端点向右走的过程中集合不断变大,但是只会变大m次。所以大胆向右跑,复杂度nmlog
用int128存一下集合状态
牛客神机,没加优化的代码1.7秒就过了。。。赛场上这个代码还要优化
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x; scanf("%lld",&x); return x; } set<__int128>ans; queue<int>pos[200010]; priority_queue<pair<int,int> >o; int n,m; int main() { n=read();m=read(); for(int i=1;i<=n;i++) for(int k=read();k;k--) pos[read()].push(i); for(int l=1;l<=n;l++) { for(int i=1;i<=m;i++) { if(pos[i].size()&&pos[i].front()<l) pos[i].pop(); if(pos[i].size()) o.push({-pos[i].front(),i}); } __int128 now=0,t=1; int r; while(o.size()) { r=o.top().first; while(o.size()&&o.top().first==r) { now=now|(t<<o.top().second); o.pop(); } ans.insert(now); } } cout<<ans.size(); }
C
正常写的话就组合数搞一搞
但是不取模,那么问题就有趣起来了
众所周知,Σc(奇数,sum)=Σ(偶数,sum),是很对称的,所以我刚开始猜了每个点的贡献只有1或-1,试了几个菊花图后发现贡献和儿子数量有关:a[x]*(x的儿子数量-1)
找找规律:
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int fa[100],d[100],sum[100]; int n; vector<int>e[100],a; int lca(int x,int y) { while(d[x]>d[y]) x=fa[x]; while(d[x]<d[y]) y=fa[y]; while(x!=y) x=fa[x],y=fa[y]; return x; } void dfs(int x) { for(auto y:e[x]) { if(y==fa[x])continue; fa[y]=x; d[y]=d[x]+1; dfs(y); } } void work(int d) { if(d==n+1) { if(a.size()==0)return ; int t=a[0]; for(auto x:a) t=lca(x,t); if(a.size()&1) sum[t]++; else sum[t]--; return ; } a.push_back(d); work(d+1); a.pop_back(); work(d+1); } int main() { n=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dfs(1); work(1); for(int i=1;i<=n;i++) cout<<sum[i]<<' '; }
考虑ai对答案的贡献,是ai*以i为lca的点集方案,偶数方案数-奇数方案数)
考虑这样的点集,要么i出现+多个子树的点,要么i出现+一个子树的点,要么i没出现+多个子树的点,要么只有i自己一个
第一种情况和第三种情况一一消掉(因为奇偶性一定相反)
还剩下i出现+一个子树的点
众所周知,在这个子树里选点,出现奇数的方案数=出现偶数的方案数
但是呢,你把出现0次的单拎出来,它对答案的贡献是+1
最后算上只有i自己一个的-1的贡献,结果为儿子数量-1
可以ac的代码(大概:
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } int n; ll sum[200010],ans; int main() { n=read(); for(int i=1;i<n;i++) sum[read()]++; for(int i=1;i<=n;i++) ans+=(sum[i]-1)*read(); cout<<ans; }
K
大胆贪心
如果有正数也有负数,就输出Σ正数*Σ负数
如果只有正数就输出(sum-最小正数)*最小正数
如果只有负数就输出(sum-最大负数)*最大负数
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x; scanf("%lld",&x); return x; } int n; ll a[200010],ans,sum0,sum1; vector<ll>q[2]; int main() { n=read(); for(int i=1;i<=n;i++) { int x=read(); if(x==0) { q[0].push_back(0); q[1].push_back(0); } else if(x<0) { sum0+=x; q[0].push_back(x); } else { sum1+=x; q[1].push_back(x); } } sort(q[0].begin(),q[0].end()); sort(q[1].begin(),q[1].end()); if(q[0].size()&&q[1].size()) ans=sum1*sum0; else if(q[0].size()) ans=(sum0-q[0].back())*q[0].back(); else ans=(sum1-q[1].front())*q[1].front(); cout<<ans; }
L
二维前缀和板子题
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x; scanf("%lld",&x); return x; } int n,m,k,sum[4][1010][1010]; int a,b,c,d; char s[1010]; int main() { n=read();m=read();k=read(); for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=1;j<=m;j++) { sum[1][i][j]=sum[1][i-1][j]+sum[1][i][j-1]-sum[1][i-1][j-1]+(s[j]=='C'); sum[2][i][j]=sum[2][i-1][j]+sum[2][i][j-1]-sum[2][i-1][j-1]+(s[j]=='M'); sum[3][i][j]=sum[3][i-1][j]+sum[3][i][j-1]-sum[3][i-1][j-1]+(s[j]=='F'); } } for(;k;k--) { a=read();b=read();c=read();d=read(); for(int i=1;i<=3;i++) printf("%d ",sum[i][c][d]-sum[i][c][b-1]-sum[i][a-1][d]+sum[i][a-1][b-1]); printf("\n"); } }
M
理解题意后发现只需要对每个商家的物品从大到小排序,选前第i大的物品做背包,体积是i,假价值是(Σai)-X。f[i][j]表示前i个商家用j个物品最多卖多少钱。复杂度m*n+n*n
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x; scanf("%lld",&x); return x; } int n,m,k; int x[1010],f[1010][1010]; vector<int>a[1010]; int main() { n=read();m=read();k=read(); for(int i=1;i<=m;i++) x[i]=read(); for(int i=1;i<=n;i++) { int v=read(); a[read()].push_back(v); } for(int i=1;i<=k;i++) f[0][i]=-1e9; for(int i=1;i<=m;i++) { for(int kk=1;kk<=k;kk++) f[i][kk]=f[i-1][kk]; sort(a[i].begin(),a[i].end(),greater<int>()); for(int j=0,cnt=1,sum=0;j<a[i].size();j++,cnt++) { sum+=a[i][j]; for(int kk=k;kk>=cnt;kk--) f[i][kk]=max(f[i][kk],f[i-1][kk-cnt]+sum-x[i]); } } cout<<f[m][k]; }