雖然因爲菜造成了這麼慢的寫完,但至少策略上沒有什麼問題,既然卡題那就做下一題,再卡就再換,雖然最後的時間都不夠做這道D題了
A、Shovels and Swords
題意簡單不表
菜是原罪,比賽的時候嘗試了各種手段,各種方法
首先要求出x+y的最大值
可以列出等式
x + 2y <= a;
2x + y <= b;
由此可知3x+3y <= (a+b)
因此x+y <= (a+b)/3
到了這步我就不會了,感覺不是充要條件,當然事後題解是和a,b再取個最小值,但我當時真懵了,感覺搞不出充要證明
就換方法
於是乎我就通過推樣例發現,本題的難點在於貪心一方是不一定能夠得到最後答案的,那麼如果能夠將兩個值變爲相等的話,那麼就有固定套路,如果不能變爲相等,直接取最小,如果可以,就判斷一下%3的情況,於是過了。
int n ;
int a,b;
int main(){
int _;
for(scanf("%d" , &_) ; _ ; _ --){
scanf("%d %d", &a , &b);
if(a > b )swap(a,b);
int x = b - a;
int y = min(x ,min(a,b/2));
b -= 2*y, a -= y;
if(b == a){
int q ;
if(a % 3 >= 2)q = 1;
else q = 0;
printf("%d\n",a / 3 * 2 + y + q);
}
else{
printf("%d\n" , y);
}
}
return 0;
}
int main(){
int _;
for(scanf("%d" ,&_); _ ;_ --){
int a,b;
scanf("%d %d" ,&a, &b);
int mini = min((a + b) / 3 , min(a,b));
cout << mini <<endl;
}
return 0;
}
B. Shuffle
題意:給你一個n,m,x。x代表初始位置,有n個數,m個區間,如果x在區間裏,你可以將x替換成區間內的任意一個值,問你最後可能的最大範圍是多少
思路:大致就是x在某個區間裏,就將x擴大爲這個區間,然後利用這個區間再更新
int main(){
int _;
for(scanf("%d" , &_) ; _ ; _ --){
int n , m , x , l ,r;
scanf("%d %d %d" ,&n , &x, &m);
int maxx = -1 , mini = 0x3f3f3f3f , flag = 0;
for(int i = 1; i <= m ; i ++){
scanf("%d %d",&l , &r);
if(flag){
if((l >= mini && l <= maxx)||(r >=mini && l <= maxx)){
maxx = max(r, maxx);
mini = min(l, mini);
}
}
if(l <= x && r >= x){
maxx = max(r, maxx);
mini = min(l, mini);
flag = 1;
}
}
if(!flag)puts("1");
else{
cout << maxx - mini + 1 << "\n";
}
}
}
C. Palindromic Paths
題意:你從1,1點到n,m點的所有路徑必須是迴文路徑
思路:類似於一個bfs的思想,第一步和最後一步的數字必須相同,第二步和倒數第二步…以此類推,再將裏面的值肯定是取數量最大的值,改變數量最小的值。
int a[33][33];
int dx[2] = {1,0},
dy[2] = {0,1};
int v[100][3];
int main(){
int _;
for(scanf("%d" , &_) ; _ ; _ --){
int n , m;
memset(v,0,sizeof v);
scanf("%d %d" ,&n ,&m );
for(int i = 1; i <= n ; i++){
for(int j = 1; j <= m ; j ++){
scanf("%d",&a[i][j]);
v[abs(i-1) + abs(j-1)][a[i][j]] ++;
}
}
int ans = 0;
for(int i = 0 ,j = n+m-2 ; i < j ; i ++ , j --){
// int maxx = max(v[i][0]+v[j][0],v[i][1] + v[j][1]);
ans += min(v[i][0]+v[j][0],v[i][1] + v[j][1]);
}
printf("%d\n",ans);
}
}
D. Two Divisors
來到重點題,題意好像還是很輕鬆明白的
重要的是這題的思路證明。
首先對於這一題的求解,想要尋找問題的突破口,於是就必須看給出的條件,他d1,d2的限制是在每一個a[i]的所有因子裏,再加上 他的每一個a[i] 是在1e7裏的,我們很容易想到一個n*sqrt(A)的做法,將每一個數的因子找出來,然後找兩個符合的,我們這時候會發現n是5e5而sqrt(A)大約在5e3左右綜合複雜度大約在1e9左右,是不能跑過cf的樣例的。到了這裏我們會繼續看,其實對於d1+d2想要和a[i]取到一個gcd爲1,我們可以這樣,感性認知一下,對於每一個數分解成一個數乘以另一個數,這倆個數相加一定會符合gcd爲1
下面給出證明
- 假設A分解成p1c1 p2c2 p3c3…
將他分成
x = p1c1p2c2…pkck ,
y = pk+1ck+1pk+2ck+2…
因爲xy = n
那麼
gcd(x+y,n) = gcd(x+y,xy) = gcd(x+y,x)*gcd(x+y,y) = gcd(x,y)*gcd(y,x) = 1
命題得證
(這個格式用法好迷啊=-=不會用)
那麼現在我們只需要維護1e7以內所有數的最小公因數,並且只要爲兩個就可以輸出,否則就是-1,兩種方法(實際上還有很多種隨你用,線性篩蠻快的)
int n;
int a[N];
int v[10000007];
int main(){
for(int i = 2 ; i <= M ; i ++){
if(v[i] == 0){
v[i] = i;
for(int j = i + i ; j <= M ; j +=i){
v[j] = i;
}
}
}
while(~scanf("%d" , &n)){
for(int i = 1; i <= n ; i ++){
scanf("%d" , &a[i]);
}
VI G[2];
for(int i = 1; i <= n ; i ++){
int res = a[i] , lin = v[res];
while(res % lin == 0 )res /= lin;
if(res > 1){
G[0].pb(res);
G[1].pb(a[i]/res);
continue;
}
G[0].pb(-1);
G[1].pb(-1);
}
for(auto x : G[0])printf("%d ",x);puts("");
for(auto x : G[1])printf("%d ",x);puts("");
}
return 0;
}
int n;
int a[N];
int v[10000007],prime[10000007];
int main(){
int m = 0;
for(int i = 2 ; i <= M ; i ++){
if(v[i] == 0){
v[i] = i;
prime[++m] = i;
}
for(int j = 1 ; j <= m ;j ++){
if(prime[j]>v[i]||prime[j]>M/i)break;
v[i*prime[j]] = prime[j];
}
}
// for(int i = 1; i <= 100; i ++)cout << prime[i] <<" ";puts("");
while(~scanf("%d" , &n)){
for(int i = 1; i <= n ; i ++){
scanf("%d" , &a[i]);
}
VI G[2];
for(int i = 1; i <= n ; i ++){
int res = a[i] , lin = v[res];
while(res % lin == 0 )res /= lin;
if(res > 1){
G[0].pb(res);
G[1].pb(a[i]/res);
continue;
}
G[0].pb(-1);
G[1].pb(-1);
}
for(auto x : G[0])printf("%d ",x);puts("");
for(auto x : G[1])printf("%d ",x);puts("");
}
return 0;
}