总结放前面方便之后看自己总体上做的不好的地方,以便改正。
因为在实习,没有那么快开题,一进去后看到J题过的人多,就去做J,结果花了足足1小时才过。之后一直在肝B,以为是一道找规律加递归版逆元公式套过,结果没解出来。然后想套个simpson自适应来找规律,还是莫得办法。到第三个小时心态有点崩,看了概率的F,没积出来,靠,心态崩了。
总的来说,这次应该是提醒下次要全程状态在线,同时一道题如果快半个小时也没找到思路就要先去做其他的题目,以便及时找到自己擅长的领域(线段树,数论以及基础dp)。同时数论不能只学知识点,虽然FFT、母函数、以及基本的涉及素数的全部算法都学懂了(详见KineXence的数论博客),但做的题目少,导致这次遇到数学题还是没有自信去开。不仅要去学数学知识,还要结合算法题目,这是这次比赛的教训。(另外看完题目再去思考,往往只看到中叶的思路跟题目的解法会有出入)
话不多说,开始补题:
分析题。首先假设长度为len的区间符合条件(区间设为ABC),那么len++的时候,新增的区间为D,CD,BCD,ABCD,判断这些区间是否符合条件。所以设一个j-1,往回跑判断即可。当a[D]>a[j]&&b[D]>b[j]时,D在新增区间AB…j中一定不是最小的,那么直接跳出循环。
代码:
#include
using namespace std;
const int MAX_N = 1e5 + 5;
int a[MAX_N],b[MAX_N];
int main()
{
int n;
while(scanf("%d",&n)==1){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
bool flag = 1;
int i;
for(i=1;i<=n;i++){
for(int j=i-1;j>0;j--){
if(a[i]>a[j]&&b[i]>b[j]) break;
else if((a[i]>a[j]&&b[i]<b[j])||(a[i]<a[j]&&b[i]>b[j])){
flag = 0;
break;
} //最小值的下标改变的点不同
else {} //表示最小值下标都改到了i的位置,符合条件,但得继续判断完剩余区间
}
if(flag==0) break;
}
printf("%d\n",i-1);
}
return 0;
}
自己sb题。二十分钟没想出来就得立即换,以免没时间做自己会做的题目。
除了是自己sb,这套题还是个数学题。这个题目给了我提醒,像这种数学构造题目一般如果FFT和母函数不能解决的,那么一般不可能可以用规律推出。那么我们得根据题目所给的条件,将目标函数转化成题目所给的条件的形式。
只有累加才能拆开积分,那么我们假设被积分的函数 1 ∏ ( a i 2 + x 2 ) = c 1 ( a 1 2 + x 2 ) + c 2 ( a 2 2 + x 2 ) + . . . c n ( a n 2 + x 2 ) \frac{1}{\prod(a_i^2+x^2)} =\frac{c_1}{(a_1^2+x^2)}+\frac{c_2}{(a_2^2+x^2)}+...\frac{c_n}{(a_n^2+x^2)} ∏(ai2+x2)1=(a12+x2)c1+(a22+x2)c2+...(an2+x2)cn,写到这步应该知道要求出 c i c_i ci的值来对其构造了。记住,在数学等式中引入虚数是没问题的,只要等式成立。先求 c 1 c_1 c1,等式两边相乘 ( a 1 2 + x 2 ) (a_1^2+x^2) (a12+x2),发现令 x 2 = − a 1 2 x^2 = -a_1^2 x2=−a12可以消掉其他的系数。那么就有 c 1 = 1 ∏ k = 1 a n d k ! = i n ( a k 2 − a i 2 ) c_1 = \frac{1}{\prod_{k=1andk!=i}^{n}(a_k^2-a_i^2)} c1=∏k=1andk!=in(ak2−ai2)1,是个 O ( n 2 ) O(n^2) O(n2) 的算法。题目提示 n 2 < 1 0 7 n^2<10^7 n2<107,复杂度可以接受。那么我们的式子就转化为: 1 π ∫ 1 ∏ ( a i 2 + x 2 ) d x = ∑ 1 2 a i ∗ c i \frac{1}{\pi}\int{\frac{1}{\prod(a_i^2+x^2)}}dx=\sum{\frac{1}{2a_i}*c_i} π1∫∏(ai2+x2)1dx=∑2ai1∗ci
代码:
#include
using namespace std;
const int MOD = 1e9 + 7;
typedef long long LL;
long long inv(long long a)
{
if(a == 1)
return 1;
return (MOD-MOD/a)*inv(MOD%a)%MOD;
}
LL qkpow(LL a,LL p,LL mod)
{
LL t=1,tt=a%mod;
while(p)
{
if(p&1)t=t*tt%mod;
tt=tt*tt%mod;
p>>=1;
}
return t;
}
LL getInv(LL a,LL mod)
{
return qkpow(a,mod-2,mod);
}
int main()
{
int n;
long long a[1005] = {0};
while(scanf("%d",&n)==1){
long long sum = 0;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++){
long long ind = 1LL;
for(int k=1;k<=n;k++){
if(k==i) continue;
ind *= ((a[k]*a[k]%MOD - a[i]*a[i]%MOD)%MOD+MOD)%MOD,ind %= MOD;
}
ind = inv(ind) * inv(2)%MOD*inv(a[i])%MOD;
sum += ind,sum %= MOD;
}
if(sum<0) sum+=MOD;
printf("%lld\n",sum);
}
return 0;
}
这道题要注意,理论上只有1~MOD-1才有逆元值,所以在用递推逆元函数的时候要保证里面的参数严格小于MOD值(取模后再求逆元)。同时 i n v ( a 1 ) ∗ i n v ( a 2 ) ∗ . . . ∗ i n v ( a n ) = i n v ( a 1 ∗ a 2 ∗ . . . ∗ a n M O D p ) inv(a_1)*inv(a_2)*...*inv(a_n) = inv(a_1*a_2*...*a_nMODp) inv(a1)∗inv(a2)∗...∗inv(an)=inv(a1∗a2∗...∗anMODp),所以用后面的方法可以节省不少复杂度。
dp我构建的模型不太多,这次学到了如何用dp来表示字符串是否合法。
我们对这道题进行分析,有n个AB和m个BA,那么一定有前n个A是属于AB的,同样有前m个B是属于BA的。对于合法解中有重叠子问题,那么我们用动态规划的思路来考虑。我们构建一个状态方程式 d p [ i ] [ j ] dp[i][j] dp[i][j]代表已选上i个A与j个B。
状态转移分析:当我们的i
同理B的判断也跟A相同。边界值: d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1,n=m=0有且仅有一个解。
废话不多说,上代码:
#include
using namespace std;
const int MAX_N = 2e3 + 5;
int dp[MAX_N][MAX_N];
const int MOD = 1e9 + 7;
void init(int n,int m)
{
int len = n + m;
for(int i=0;i<=len;i++){
for(int j=0;j<=len;j++){
dp[i][j] = 0;
}
}
dp[0][0] = 1; //n=0,m=0有且仅有一解
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==2){
init(n,m);
int len = n+m;
for(int i=0;i<=len;i++){
for(int j=0;j<=len;j++){
if(i<n||(i-n)<min(j,m)){
dp[i+1][j] += dp[i][j];
dp[i+1][j] %= MOD;
}
if(j<m||(j-m)<min(i,n)){
dp[i][j+1] += dp[i][j];
dp[i][j+1] %= MOD;
}
//printf("%d\n",dp[i][j]);
}
}
printf("%d\n",dp[len][len]);
}
return 0;
}
int的值在2e9+1e8左右,所以不需要开long long。曾经有个学长开long long做题超时了,可以用int就不要用LL。
有一学长用等边三角形进行积分…说实话还真的很方便。设等边三角形的底长为L,那么我们可以通过二重积分写出关于L的式子: E = ∫ − 1 4 L 0 ∫ − 3 3 x − 3 12 L 3 x + 3 4 L L ∗ y + 3 8 L 2 E = \int_{-\frac{1}{4}L}^{0}\int_{-\frac{\sqrt{3}}{3}x-\frac{\sqrt{3}}{12}L}^{\sqrt{3}x+\frac{\sqrt{3}}{4}L}L*y+\frac{\sqrt{3}}{8}L^2 E=∫−41L0∫−33x−123L3x+43LL∗y+83L2,积出来的结果与用 L = 4 3 S 3 L=\sqrt{\frac{4\sqrt{3}S}{3}} L=343S带入,然后根据三点坐标求出S,代入其中*36即可。结果是 11 / 2 ∗ S 11/2*S 11/2∗S
代码:
#include
using namespace std;
typedef long long ll;
int main()
{
ll x1,y1,x2,y2,x3,y3;
while(scanf("%lld%lld%lld%lld%lld%lld",&x1,&y1,&x2,&y2,&x3,&y3)==6){
ll a = x2 - x1, b = y2 - y1;
ll c = x3 - x1, d = y3 - y1;
ll ans = abs(a * d - b * c);
printf("%lld\n",ans*11);
}
return 0;
}
跑了LL和long double都没过,考虑叉乘,但是数据肯定会爆1e18。然鹅。。。看了RANK1的组,居然有一个__int128…网上搜了一下,输入输出流是没有重载__int128类型的,所以输入输出需要自己写,但它确实也能储存1e35的数字。但这道题输入最多LL型,而输出只需要输出字符,同时__int128支持比较,所以可以这么写…
代码1:
#include
using namespace std;
typedef long long LL;
int main()
{
int a,b;
LL x,y;
while(scanf("%lld%d%lld%d",&x,&a,&y,&b)==4){
__int128 l = (__int128)x * b;
__int128 r = (__int128)y * a;
if(l>r){
printf(">");
}
else if(l<r){
printf("<");
}
else{
printf("=");
}
printf("\n");
}
return 0;
}