nflsoi 8.16 题解
数论场,难调场,评测机崩溃场,复活场。
C.#12322 幂次整除 / TopCoder 12870 BigFatInteger2
题意
给定四个整数 a,b,c,da,b,c,da,b,c,d,判断 aba^bab 是否为 cdc^dcd 的倍数,是输出 divisible
,否则输出 not divisible
。
A,B,C,D∈[1,109]A,B,C,D\in[1,10^9]A,B,C,D∈[1,109]
思路
就是这题害得 20035 崩溃。
这题值域太大,预处理质数肯定超时,所以宁愿 O(V)O(\sqrt{V})O(V) 在线判断,顶多减少判断次数。
先特判:
- 对于 111 的情况,c=1c=1c=1 可以,a=1a=1a=1 且 c>1c>1c>1 不行;
- 幂次相等的情况,a<ca<ca<c 不行;
- a,c>1a,c>1a,c>1,两数互质不行;
- 设 g=gcd(a,c)g=\gcd(a,c)g=gcd(a,c),对于 ccc 的剩余因子 c/gc/gc/g,如果 gcd(a,c/g)=1\gcd(a,c/g)=1gcd(a,c/g)=1,即 ccc 存在 aaa 没有的质因子,就不行。
接下来就是分解质因数和幂次,然后判断相同质因子 aaa 的幂次和是否大于 ccc 的即可。
其实这题不难,但是测评太久了,而且容易错,搞人心态。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define sayno {puts("not divisible");return 0;}
#define sayes {puts("divisible");return 0;}
const ll N=1e6+9;
ll a,b,c,d;
ll g;
ll f1[N],f2[N],k1[N],k2[N],c1,c2;
bool isprime(ll x)
{for(int i=2;i*i<=x;i++)if(x%i==0)return 0;return 1;
}
int main()
{scanf("%lld%lld%lld%lld",&a,&b,&c,&d);if(c==1)sayesif(a==1&&c>1)saynoif(b==d&&a<c)saynoll g=__gcd(a,c);if(g==1)saynoll A=a/g,C=c/g;if(C>1&&__gcd(a,C)==1)saynofor(ll i=2;a>1;i++){if(isprime(a)){f1[++c1]=a;k1[c1]=1;break;}ll tot=0;while(a%i==0){tot++;a/=i;}if(tot)f1[++c1]=i,k1[c1]=tot;}for(ll i=2;c>1;i++){if(isprime(c)){f2[++c2]=c;k2[c2]=1;break;}ll tot=0;while(c%i==0){tot++;c/=i;}if(tot)f2[++c2]=i,k2[c2]=tot;}if(c1<c2)saynoll pos=1;for(int i=1;i<=c2;i++){while(pos<=c1&&f1[pos]<f2[i])pos++;if(f1[pos]!=f2[i])saynoif(k1[pos]*b<k2[i]*d)sayno}sayesreturn 0;
}
/*
75
1
5
2
*/
D.#16983 完整正方形 / AT_abc311_e Defect-free Squares
题意
有一个高为 HHH 行、宽为 WWW 列的网格。网格中从上往下第 iii 行、从左往右第 jjj 列的格子记作 (i,j)(i, j)(i,j)。
网格中的每个格子要么有洞,要么没有洞。恰好有 NNN 个格子有洞,这些格子的位置分别为 (a1,b1),(a2,b2),…,(aN,bN)(a_1, b_1), (a_2, b_2), \dots, (a_N, b_N)(a1,b1),(a2,b2),…,(aN,bN)。
当正整数三元组 (i,j,n)(i, j, n)(i,j,n) 满足以下条件时,以 (i,j)(i, j)(i,j) 为左上角、(i+n−1,j+n−1)(i + n - 1, j + n - 1)(i+n−1,j+n−1) 为右下角的正方形区域被称为没有洞的正方形:
- i+n−1≤Hi + n - 1 \leq Hi+n−1≤H
- j+n−1≤Wj + n - 1 \leq Wj+n−1≤W
- 对于所有满足 0≤k≤n−1,0≤l≤n−10 \leq k \leq n - 1, 0 \leq l \leq n - 10≤k≤n−1,0≤l≤n−1 的非负整数对 (k,l)(k, l)(k,l),(i+k,j+l)(i + k, j + l)(i+k,j+l) 这个格子没有洞。
请问网格中一共有多少个没有洞的正方形?
1≤H,W≤30001 \leq H, W \leq 30001≤H,W≤3000,0≤N≤min(H×W,105)0 \leq N \leq \min(H \times W, 10^5)0≤N≤min(H×W,105),(ai,bi)(a_i, b_i)(ai,bi) 互不相同。
思路
看到这题单调栈应激了,怎么跟这题还在同一场 abc?
考虑枚举 (i,j)(i,j)(i,j) 为右下角,有多少个正方形。想要悬线法吗?想要单调栈吗?怎么确定 min(Li,j,upi,j)\min(L_{i,j},up_{i,j})min(Li,j,upi,j) 边长的正方形里面没有洞呢?
还是 dp 吧,其实也不难想。设 fi,jf_{i,j}fi,j 表示以 (i,j)(i,j)(i,j) 为右下角的正方形有多少个。我们并入 i−1i-1i−1 行或者 j−1j-1j−1 列的信息,正方形的个数也就多出一个,因此是这其中某一个 +1+1+1。
怎么处理洞呢?首先如果 (i,j)(i,j)(i,j) 有洞那么 fi,j=0f_{i,j}=0fi,j=0,假若从 (i,j)(i,j)(i,j) 向 (i+1,j)(i+1,j)(i+1,j) 转移,因为 (i+1,j)(i+1,j)(i+1,j) 上面有个洞,所以不管左上状态 fi−1,jf_{i-1,j}fi−1,j 和左侧状态 fi,j−1f_{i,j-1}fi,j−1 状态有多大,(i,j)(i,j)(i,j) 为右下角的合法正方形边长只能为 111。
因此有洞的点相当于给转移搞了个上界,即转移到 fi,jf_{i,j}fi,j 是要在 fi,j−1,fi−1,j,fi−1,j−1f_{i,j-1},f_{i-1,j},f_{i-1,j-1}fi,j−1,fi−1,j,fi−1,j−1 中找一个最小的上界来转移,即:
fi,j=min(fi,j−1,fi−1,j,fi−1,j−1)+1f_{i,j}=\min(f_{i,j-1},f_{i-1,j},f_{i-1,j-1})+1fi,j=min(fi,j−1,fi−1,j,fi−1,j−1)+1
答案即 ∑fi,j\sum f_{i,j}∑fi,j
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3004,M=1e5+9;
ll h,w,n;
bool vis[N][N];
ll f[N][N];
int main()
{scanf("%lld%lld%lld",&h,&w,&n);for(int i=1;i<=n;i++){ll x,y;scanf("%lld%lld",&x,&y);vis[x][y]=1;}ll ans=0;for(int i=1;i<=h;i++){for(int j=1;j<=w;j++){if(vis[i][j])f[i][j]=0;//断左断上 else f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;ans+=f[i][j];}}printf("%lld",ans);return 0;
}
E.#25020 偏移量 / CF1152C Neko does Maths
题意
Neko 有两个整数 aaa 和 bbb。他的目标是找到一个 k≥0k\ge 0k≥0,使得 a+ka+ka+k 和 b+kb+kb+k 的最小公倍数尽可能小。如果有多个最优的 kkk,他需要选择最小的那个。
1≤a,b≤1091\le a,b\le 10^91≤a,b≤109。
思路
带个 kkk 求 lcm(a+k,b+k)=(a+k)(b+k)gcd(a+k,b+k)\mathrm{lcm}(a+k,b+k)=\dfrac{(a+k)(b+k)}{\gcd(a+k,b+k)}lcm(a+k,b+k)=gcd(a+k,b+k)(a+k)(b+k) 很麻烦,但是如果我辗转相除呢?
考虑求 gcd(a+k,b−a)\gcd(a+k,b-a)gcd(a+k,b−a)(钦定 a<ba<ba<b),此时 b−ab-ab−a 为定值而且因数只能在其因数里取到。枚举一个因数 xxx,那么存在一个合法的 kkk 为 k=x−amodxk=x-a\bmod xk=x−amodx 使 a+ka+ka+k 成为 xxx 的倍数。此时判断 gcd(a+k,b+k)\gcd(a+k,b+k)gcd(a+k,b+k) 是否恰为 xxx,以及其 lcm\mathrm{lcm}lcm 是否能取得更小即可。时间复杂度 O(b−alogV)O(\sqrt{b-a}\log V)O(b−alogV),logV\log VlogV 约为做 gcd\gcdgcd 的值域。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll inf=3e14;
ll a,b;
ll cha;
ll cal(ll a,ll b,ll k,ll d)
{return (a+k)/d*(b+k);
}
int main()
{scanf("%lld%lld",&a,&b);if(a>b)swap(a,b);cha=b-a;ll ans=0,mi=a*b/__gcd(a,b);for(int i=1;i*i<=cha;i++){if(cha%i==0){ll x=i,y=cha/i;ll k=x-a%x;if(__gcd(a+k,b+k)==x){if(cal(a,b,k,x)<mi){mi=cal(a,b,k,x);ans=k;}}if(x==y)continue;k=y-a%y;if(__gcd(a+k,b+k)==y){if(cal(a,b,k,y)<mi){mi=cal(a,b,k,y);ans=k;}}}}printf("%lld",ans);return 0;
}
F.#8429 小学数学 / BZOJ4429 Nwerc2015 Elementary Math 小学数学
题意
Ellen 正在教她的学生小学数学,期末考试的时间到了。考试包含n道题目,每道题要求学生对一对数字进行加(+
)、减(-
)或乘(*
)运算。
Ellen已经选好了 nnn 对数 (a,b)(a,b)(a,b),现在需要为每对数决定应该使用哪种运算。为了避免学生感到厌烦,Ellen希望确保这n道题的正确答案都不相同。
请帮助Ellen自动完成考试的构建工作。你可以输出任意一个。如果没有有效答案,则输出一行字符串"impossible"。
输出的运算符前后都有空格,1≤n≤25001\le n\le 25001≤n≤2500,∣a∣,∣b∣≤106|a|,|b|\le 10^6∣a∣,∣b∣≤106。
思路
数对进行三种运算会有三种运算结果,不同的数对经过不同的运算可能会有相同的结果,我们需要这些结果不相同,即 nnn 个数对能够恰好配对若干个结果。
这不就是二分图匹配吗?把结果离散化成点然后跑匈牙利算法去匹配即可。
(说着容易,实则不算难调,但是题目卡我输出格式、不能分开输出数和符号什么意思?喜提 333 页提交记录)
代码
//BZOJ4429
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=5504;
ll n;
struct term
{ll x,y;ll add,minus,times;
}a[N];
map<ll,ll>mp;
vector<ll>G[N*5];
ll val[N*5],op[N*5];
bool vis[N*5];
ll match[N*5],pp[N];
bool dfs(ll u)
{for(auto v:G[u]){if(vis[v])continue;vis[v]=1;if(match[v]==0||dfs(match[v])){match[v]=u;pp[u]=v;return 1;}}return 0;
}
struct pout
{ll x,y,op,val,id;
}b[N*5];
bool cmp(pout x,pout y)
{return x.id<y.id;
}
ll vv[N*5],nv,nnv;
int main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){ll x,y;scanf("%lld%lld",&x,&y);a[i]=(term){x,y,x+y,x-y,x*y};}ll pos=n;for(int i=1;i<=n;i++){// cout<<i<<":"<<a[i].x<<" "<<a[i].y<<endl;// cout<<a[i].add<<"\n"<<a[i].minus<<"\n"<<a[i].times<<"\n\n";if(!mp[a[i].add])mp[a[i].add]=++pos,val[pos]=a[i].add,op[pos]=1; if(!mp[a[i].minus])mp[a[i].minus]=++pos,val[pos]=a[i].minus,op[pos]=2;if(!mp[a[i].times])mp[a[i].times]=++pos,val[pos]=a[i].times,op[pos]=3;G[i].push_back(mp[a[i].add])/*,cout<<i<<"->"<<mp[a[i].add]<<"\n"*/;G[i].push_back(mp[a[i].minus])/*,cout<<i<<"->"<<mp[a[i].minus]<<"\n"*/;G[i].push_back(mp[a[i].times])/*,cout<<i<<"->"<<mp[a[i].times]<<"\n"*/;}ll cnt=0;for(int i=1;i<=n;i++){memset(vis,0,sizeof(vis));if(dfs(i))cnt++;else break;}if(cnt<n){puts("impossible");return 0;}for(int i=1;i<=n;i++){ll ret;char op;if(val[pp[i]]==a[i].add)op='+';if(val[pp[i]]==a[i].minus)op='-';if(val[pp[i]]==a[i].times)op='*';cout<<a[i].x<<" "<<op<<" "<<a[i].y<<" = "<<val[pp[i]]<<"\n";}return 0;
}