标签:接下来 friend 修改 add 表示 struct wal com ret
最后四天,最后四场!
有手就行,考虑偶数部分和多出的一个\(0/1\)分别用什么买最优即可
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int a,b,c,d,n;
int main()
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&n);
return printf("%lld",1LL*(n/2)*min(8*a,min(4*b,min(2*c,d)))+(n&1)*min(4*a,min(2*b,c))),0;
}
刚开始想的贼复杂,后来被陈指导提示了才会做
考虑可操作的区间数量是\(\frac{n(n+1)}{2}+1\),我们考虑哪些区间是不合法的
直观地,若\([l,r]\)满足\(s_l=s_r\),那么他得到的结果和\([l+1,r-1]\)显然是相同的
因此我们对于每种字符求出它出现的个数统计一下对数即可
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,c[26]; char s[N]; long long ans;
int main()
{
RI i; for (scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) ++c[s[i]-‘a‘];
for (ans=1LL*n*(n+1)/2LL+1,i=0;i<26;++i) ans-=1LL*c[i]*(c[i]+1)/2LL;
return printf("%lld",ans),0;
}
去年大概这个时候模拟赛考过,一年后再写感觉细节也挺少的
首先一个直观的想法,在直接走过去的路上走尽量多的\(\frac{1}{4}\)圆可以使路径长度减少\(20-5\pi\)
假设\(x_1<x_2,y_1<y_2\),我们把所有点都按\(x_i\)排序之后找出在两点之间的点,按\(y_i\)做一个LIS就是能经过的最多的\(\frac{1}{4}\)个数了,\(y_1\ge y_2\)同理
但是我们发现有些情况一定会经过半圆,画画图分析下此时若LIS长度\(<\min(|x_1-x_2|,|y_1-y_2|)+1\)那必然要经过一个半圆,多走\(10\pi -20\)的路
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
const double pi=acos(-1);
struct point
{
int x,y;
friend inline bool operator < (const point& A,const point& B)
{
return A.x<B.x;
}
inline void input(void) { scanf("%d%d",&x,&y); }
}p[N],s,t; int rst[N],n,ans,tp;
#define lowbit(x) (x&-x)
class Tree_Array1
{
private:
int bit[N];
public:
inline int get(RI x,int ret=0)
{
for (;x;x-=lowbit(x)) ret=max(ret,bit[x]); return ret;
}
inline void add(RI x,CI y)
{
for (;x<=n;x+=lowbit(x)) bit[x]=max(bit[x],y);
}
}BIT1;
class Tree_Array2
{
private:
int bit[N];
public:
inline int get(RI x,int ret=0)
{
for (;x<=n;x+=lowbit(x)) ret=max(ret,bit[x]); return ret;
}
inline void add(RI x,CI y)
{
for (;x;x-=lowbit(x)) bit[x]=max(bit[x],y);
}
}BIT2;
#undef lowbit
int main()
{
RI i; s.input(); t.input(); if (s.x>t.x) swap(s,t);
for (scanf("%d",&n),i=1;i<=n;++i) p[i].input(),rst[i]=p[i].y;
for (sort(rst+1,rst+n+1),i=1;i<=n;++i) p[i].y=lower_bound(rst+1,rst+n+1,p[i].y)-rst;
for (sort(p+1,p+n+1),i=1;i<=n;++i)
if (p[i].x>=s.x&&p[i].x<=t.x&&rst[p[i].y]>=min(s.y,t.y)&&rst[p[i].y]<=max(s.y,t.y))
{
if (s.y<=t.y) ans=max(ans,tp=BIT1.get(p[i].y)+1),BIT1.add(p[i].y,tp);
else ans=max(ans,tp=BIT2.get(p[i].y)+1),BIT2.add(p[i].y,tp);
}
double ret=100LL*(abs(s.x-t.x)+abs(s.y-t.y));
if (ans<min(abs(s.x-t.x),abs(s.y-t.y))+1) printf("%.15lf",ret-ans*(20-5*pi));
else printf("%.15lf",ret-(ans-1)*(20-5*pi)+10*pi-20); return 0;
}
都是陈指导指导的好,考虑一个\(O(n^3)\)暴力,枚举向左位移次数和向右位移次数,此时每个位置要变成什么已经确定了,预处理出每个位置左/右边最近的\(1\)再枚举每个位置判断下即可
然后我们发现每个位置最后变成什么之和左位移次数和向右位移次数的差值有关,因此我们考虑先枚举差值
接下来需要一点转化,因为直接枚举左位移次数不方便我们检验,我们枚举左位移次数的极值,即向左最远能到哪里
设每个位置\(i\)左/右边最近的\(1\)与该位置的距离为\(l_i,r_i\),此时枚举的左位移次数的极值为\(ml\),那么对于每个要修改的点只需要讨论下:
很显然把\((l_i,r_i)\)按\(l_i\)排序再从大到小扫一边即可,但由于它们的值域也是\(O(n)\)的因此直接开个桶即可
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=2005;
int n,ans=1e9,l[N],r[N],v[N]; char a[N],b[N];
int main()
{
RI i,j; scanf("%s%s",a,b); n=strlen(a);
for (i=0;i<n;++i)
{
for (j=i;b[j]!=‘1‘&&l[i]<n;j=(j-1+n)%n,++l[i]);
for (j=i;b[j]!=‘1‘&&r[i]<n;j=(j+1)%n,++r[i]);
if (l[i]==n)
{
bool flag=1; for (j=0;j<n;++j) flag&=a[j]==‘0‘;
puts(flag?"0":"-1"); return 0;
}
}
for (i=0;i<n;++i)
{
for (j=0;j<n;++j) v[j]=0; int ct=0;
for (j=0;j<n;++j) if (a[j]!=b[(j+i)%n]) ++ct,v[l[j]]=max(v[l[j]],r[j]);
int mr=0; for (j=n-1;~j;--j)
{
if (mr<=i) ans=min(ans,ct+2*j+i); else ans=min(ans,ct+2*j+mr+mr-i); mr=max(mr,v[j]);
}
}
for (i=0;i<n;++i)
{
for (j=0;j<n;++j) v[j]=0; int ct=0;
for (j=0;j<n;++j) if (a[j]!=b[(j-i+n)%n]) ++ct,v[l[j]]=max(v[l[j]],r[j]);
int mr=0; for (j=n-1;~j;--j)
{
if (i>=j) ans=min(ans,ct+2*mr+i); else ans=min(ans,ct+2*mr+j+j-i); mr=max(mr,v[j]);
}
}
return printf("%d",ans),0;
}
真神的一道题,被教育了
首先转化一下题目的要求,把题目看做让\(A\)中多余的\(1\)换到\(A\)中缺少\(1\)的地方去,具体地,我们定义:
此时可以发现可以使得\(A=B\)的操作序列满足:
连接\((b_i,a_i)\),图的形态由若干由2,3类点作为端点,1类点作为中间点的有向链(即不能倒着操作一条链)
我们设\(f_{i,j}\)表示使用了\(i\)个公共点,组成了\(j\)条链的原序列(即\(a,b\))的方案数,发现转移:
由于每次转移会导致原序列的长度加\(1\),因此我们同样钦定每次新增后放在末尾
因为此时一条链可能会对应许多种原序列,我们要用不同的转移顺序来区分它们
接下来考虑统计答案,我们枚举公共点有多少个不放在链上,设一共有\(s\)个公共点,\(t\)条链,则:
总复杂度为\(\frac{|A|^2}{4}\),可以通过此题,多项式的做法就懒得写了(其实是不会)
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=10005,mod=998244353;
int n,f[N][N],fact[N],inv[N],s,t,ans; char a[N],b[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
RI i,j; for (scanf("%s%s",a+1,b+1),n=strlen(a+1),i=1;i<=n;++i)
if (a[i]==‘1‘&&b[i]==‘1‘) ++s; else if (a[i]==‘1‘&&b[i]==‘0‘) ++t;
for (init(n),f[0][0]=1,i=0;i<=s;++i) for (j=0;j<=t;++j)
(f[i][j]+=1LL*f[i-1][j]*i%mod*j%mod)%=mod,(f[i][j]+=1LL*f[i][j-1]*j%mod*j%mod)%=mod;
for (i=0;i<=s;++i) (ans+=1LL*C(s,i)*fact[i]%mod*fact[i]%mod*C(s+t,i)%mod*f[s-i][t]%mod)%=mod;
return printf("%d",ans),0;
}
标签:接下来 friend 修改 add 表示 struct wal com ret
原文地址:https://www.cnblogs.com/cjjsb/p/13916527.html