补题若干(4)
[https://atcoder.jp/contests/abc418/tasks/abc418_e](数学+容斥)
题意:
给定\(N\)个点的坐标,求能组成多少个梯形
思路:
任意两个直线,若斜率相等,可组成梯形
斜率用分数类计算避免精度丢失以及不存在等情况
这样计算会使得平行四边形(也是梯形)被计算两次
平行四边形的两条对角线线段中点相同
容斥减去
map的const auto&可以避免拷贝依次卡常
struct FRAC{//分数结构体。
int a,b;//分子和分母。
FRAC(const int _a=0,const int _b=1):a(_a),b(_b){
int g=__gcd(a,b);
a/=g,b/=g,b<0&&(a=-a,b=-b);//保证分母非负,避免比较出现问题。
}
bool operator < (const FRAC&B)const{return a*B.b<B.a*b;}//根据分数定义重载小于运算符。
bool operator == (const FRAC&B)const{return a*B.b==B.a*b;}//同上。
};
map<FRAC,int>mp;
map<pii,int>sp;
int x[2001],y[2001];
void solve(){
int n;cin>>n;
rep(i,1,n){
cin>>x[i]>>y[i];
for(int j=1;j<=i-1;j++){
mp[FRAC(y[i]-y[j],x[i]-x[j])]++;
sp[{y[i]+y[j],x[i]+x[j]}]++;
}
}
int ans=0;
for(const auto&[a,b]:mp){
ans+=b*(b-1)/2;
}
for(const auto&[a,b]:sp){
ans-=b*(b-1)/2;
}
cout<<ans;
}
[https://atcoder.jp/contests/abc413/tasks/abc413_e](贪心+分治)
题意:
给定一个序列,你只能翻转由2^i组成的区间,求任意次翻转后最小字典序的序列
思路:
发现两两区间互不关联,所以只要让单个区间字典序最小即可,关注单个区间首个数字大小然后交换区间即可:交换=2次翻转2 + 翻转1
int n;
void solve(){
cin>>n;
vector<int>a((1ll<<n)+1);
rep(i,1,(1ll<<n))cin>>a[i];
for(int len=1;len<=n;len++){
for(int i=1;i<=(1ll<<n);i+=(1ll<<len)){
if(a[i]>a[i+(1ll<<len-1)]){
for(int _=0;_<(1ll<<len-1);_++)swap(a[i+_],a[i+(1ll<<len-1)+_]);
}
}
}
for(int i=1;i<=(1ll<<n);i++){
cout<<a[i]<<' ';
}
cout<<endl;
}
[https://atcoder.jp/contests/abc403/tasks/abc403_e](tries+打标记)
题意:
给定\(n\)个字符串 , 其中\(x\)个 是字符串集合\(S_1\)的 其他是字符串集合\(S_2\)的 , 求\(S_2\)不以\(S_1\)中的字符串为前缀的字符串个数
思路:
建立字典树
每个结点维护的信息:在\(S_2\)则 此节点的cnt++
对于\(S_1\)中每一个字符串末尾字符所在位置的子树,使其减去贡献
打标记并下传,实现在线查询
int q;
const int M= 1e6 +5;
int tot;
int cnt[M];
int clear[M];
int tries[M][26];
int ans = 0;
int dfs(int p){
if(clear[p])return 0;
clear[p]=1;
int res = 0;
res += cnt[p];
for(int i=0;i<26;i++){
if(tries[p][i]){
res+=dfs(tries[p][i]);
}
}
return res;
}
void ins1(string s){
int p=0;
for(int i=0;i<s.size();i++){
int k = s[i] - 'a';
if(!tries[p][k]){
tries[p][k]=++tot;
}
p=tries[p][k];
}
if(!clear[p])ans-=dfs(p);
}
void ins2(string s){
int p=0;
for(int i=0;i<s.size();i++){
int k = s[i] - 'a';
if(!tries[p][k]){
tries[p][k]=++tot;
}
clear[tries[p][k]]=max(clear[tries[p][k]],clear[p]);
p=tries[p][k];
}
cnt[p]++;
if(!clear[p])ans++;
}
void solve(){
cin>>q;
while(q--){
int _;cin>>_;
string s;cin>>s;
if(_==1){ins1(s);}else{ins2(s);}
cout<<ans<<endl;
}
}
[https://www.luogu.com.cn/problem/solution/AT_abc402_e](期望+状压DP)
题意:
给定\(n\)个题目,每个题目有取得概率,代价,价值,求当前有x元时,得分的最大期望
思路:
设计DP状态: dp[i][j]表示 :当前还有i元,已经通过了j道题目的状态到结束状态的最大期望得分
转移时:当前题目已通过:重新再交一遍
没通过:概率加权平均值
取max即可
int n,x;
const int M=10;
int s[M],c[M],p[M];
double dp[5005][1024];
//dp[i][j]:当前剩下来的钱是i,已经通过的题目为j 之后的得分期望最大值
//dp[x][0]
void solve(){
cin>>n>>x;
rep(i,1,n){
cin>>s[i]>>c[i]>>p[i];
}
int N = (1ll<<n)-1;
for(int i = 0;i<=x;i++){
for(int S= 0 ;S<N;S++){
for(int j=0;j<n;j++){
if(c[j+1]>i)continue;
if((1ll<<j)&S){
dp[i][S]=max(dp[i][S],dp[i-c[j+1]][S]);
}else{
dp[i][S]=max(dp[i][S],0.01*p[j+1]*(dp[i-c[j+1]][S|(1ll<<j)]+s[j+1])+0.01*(100-p[j+1])*dp[i-c[j+1]][S]);
}
}
}
}
cout<<fixed<<setprecision(18)<<dp[x][0];
}
[https://atcoder.jp/contests/abc390/tasks/abc390_e](背包+暴力)
题意:
给定若干个物品,每个物品有维生素含量和卡路里含量
给定卡路里限制\(X\), 求卡路里含量不超过\(X\)时,最少的维生素含量最大值是多少
思路:
求每个维生素种类物品在卡路里限制为\(i\)的维生素含量的最大值\(dp[i]\)
暴力枚举两种维生素的卡路里含量,剩下一个维生素的卡路里含量就确定了
int n,x;
const int M =5005;
int dp1[M],dp2[M],dp3[M];
int v[M],a[M],c[M];
void solve(int *dp,int op){
rep(i,1,n){
if(v[i]!=op)continue;
for(int j=x;j>=c[i];j--){
dp[j]=max(dp[j],dp[j-c[i]]+a[i]);
}
}
}
void solve(){
cin>>n>>x;
rep(i,1,n){
cin>>v[i]>>a[i]>>c[i];
}
solve(dp1,1);
solve(dp2,2);
solve(dp3,3);
int ans=0;
for(int i=1;i<=x;i++){
for(int j=1;j<=x-i;j++){
int k = x-i-j;
ans=max(ans,min({dp1[i],dp2[j],dp3[k]}));
}
}
cout<<ans;
}
[https://atcoder.jp/contests/abc421/tasks/abc421_e](期望+记忆化搜索)
int a[7];
double ans;
map<pair<vector<int>,int>,double>f;
double dfs(vector<int>p,int s){
if(f.count({p,s})){
return f[{p,s}];
}
if(!s){
int sum=0,mx=0;
for(int i=1;i<=6;mx=max(mx,sum),sum=0,i++){
for(int x:p)if(x==a[i])sum+=x;
}
f[{p,s}]=mx;
return mx;
}
int n=5-p.size(),Pow=1;
while(n--)Pow*=6;
n=5-p.size();
double sum = 0;
for(int st = 0;st<Pow;st++){
double mx = 0.00;
int S = st;
vector<int>add;
add.clear();
add.pb(0);
for(int i=1;i<=n;i++)add.pb(a[S%6+1]),S/=6;
for(int _=0;_<(1ll<<n);_++){
vector<int>w = p;
for(int i=1;i<=n;i++){
if(_&(1ll<<i-1))w.pb(add[i]);
}
mx=max(mx,dfs(w,s-1));
}
sum+=mx;
}
f[{p,s}]=sum/Pow;return f[{p,s}];
}
void solve(){
for(int i=1;i<=6;i++)cin>>a[i];
ans = dfs({},3);
cout<<fixed<<setprecision(10)<<ans<<endl;
}

浙公网安备 33010602011771号