1. 程式人生 > 其它 >2018 ICPC Nanjing Regional Contest(M待補)

2018 ICPC Nanjing Regional Contest(M待補)

技術標籤:演算法練習2021寒假區域賽真題ICPC區域賽

A. Adrien and Austin

題意:博弈。n個石子,每次可以選連續的 1-k 個。問誰贏。

思路:簽到題。但我沒簽上來。還是開局兩小時後隊友簽上的。

AC程式碼:

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
const int MX  = 1e6+7;
int mon[505];
int main()
{
    int n,
k; cin>>n>>k; if(n == 0 || (k == 1 && n%2 == 0)) puts("Austin"); else puts("Adrien"); return 0; }

D. Country Meow

題意:題意極簡。n個三維的點。 找到空間中的一個點,使得到這n個點的最大歐氏距離最小。求最小距離。

思路:看了一眼感覺是二分套二分套二分。隊友糾正了一下,應該是三分。沒錯確實是三分。分別二分列舉三維的座標。距離變化是一個凹函式。所以三分找凹點就好了。三維座標那就套三層三分。每一層列舉一個座標。然後O(n) check 一下。資料小。時間是可以接受的。不過三分還是寫的不熟。還有模擬退火和最小圓覆蓋。待補待補!!

AC程式碼:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+7;
struct node{
    double x,y,z;
}p[1005];
int n;
 
double dis(double xx,double yy,double zz,int i){
    double x = xx-p[i].x;
    double y = yy-p[i].y;
    double z = zz-p[i].z;
    return sqrt(x*x+y*
y+z*z); } double check(double xx,double yy,double zz){ double res = 0; for(int i = 0 ;i < n ; i ++){ res = max(dis(xx,yy,zz,i),res); } return res; } double s3(double xx,double yy){ double l = -1e5+7; double r = 1e5+7; const double EPS = 1e-9; double res = 1e18; while(r - l > EPS) { double lmid = l + (r - l) / 3; double rmid = r - (r - l) / 3; double lans = check(lmid,xx,yy),rans = check(rmid,xx,yy); // 求凹函式的極小值 res = min(lans,rans); if(lans <= rans) r = rmid; else l = lmid; } return res; } double s2(double xx){ double l = -1e5+7; double r = 1e5+7; const double EPS = 1e-9; double res = 1e18; while(r - l > EPS) { double lmid = l + (r - l) / 3; double rmid = r - (r - l) / 3; double lans = s3(lmid,xx),rans = s3(rmid,xx); // 求凹函式的極小值 res = min(lans,rans); if(lans <= rans) r = rmid; else l = lmid; } return res; } double s1(){ double l = -1e5+7; double r = 1e5+7; const double EPS = 1e-9; double res = 1e18; while(r - l > EPS) { double lmid = l + (r - l) / 3; double rmid = r - (r - l) / 3; double lans = s2(lmid),rans = s2(rmid); // 求凹函式的極小值 res = min(lans,rans); if(lans <= rans) r = rmid; else l = lmid; } return res; } signed main(){ cin>>n; for(int i = 0 ; i < n ; i ++) cin>>p[i].x>>p[i].y>>p[i].z; printf("%.15f",s1()); return 0; }

G. Pyramid

題意:數n層有多少個三角形。斜著的也算!!

在這裡插入圖片描述

思路:先手畫幾個。測試資料這麼多,顯然是要找O(1)的公式。剛開始想的是求邊長為1的個數,為2的個數。。。然後加起來。越推越複雜。然後發現直接找答案的方程不就完了嘛。一個很經典的想法。

原數列:0,1,5,15,35,70,126,210…
做差:1,4,10,20,35,56…
再做差:3,4,5,6,7…
再做差:1,1,1,1…

終於相等了。做差n次之後相等。說明,答案計算公式的最高次是n+1次。

然後就帶值解出係數就好了 ans = ax4+bx3+cx2+dx+e

不過有人直接找出 ans =n * (n + 1) * (n + 2) * (n + 3) / 24 ,amazing啊。

AC程式碼:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+7;
 
int qpow(int a,int b){
    int res =1;
    int tmp = a;
    while(b){
        if(b&1) res = (res*tmp) %mod;
        tmp = (tmp*tmp)%mod;
        b >>= 1;
    }
    return res;
}
 
int cal(int n){
    return ((((qpow(n, 4)+6*qpow(n, 3))%mod+11*qpow(n, 2))%mod+6*n)%mod)*qpow(24, mod-2)%mod;
}
 
signed main(){
    int t;
    scanf("%lld",&t);
    for(int i = 0 ; i < t ; i ++){
        int n;
        scanf("%lld",&n);
        printf("%lld\n",cal(n));
    }
    return 0;
}

I. Magic Potion

題意:n 個 hero。m 個 monster。每個英雄有一個可以擊敗的怪物列表。但是最多隻能殺一個。然後還有k個魔法藥水。魔法藥水可以讓一個 英雄 多擊殺一個怪物。每個人最多喝一瓶。

思路:顯然是二分圖匹配。但是最開始想的是。先求一個最大匹配,然後把匹配過的刪掉,再匹配一次。和k取min。但是wa了。因為刪掉一個點損失太大了,就沒有了和別人匹配的機會。不過還好很快救回來了。把每個英雄拆成兩個。然後再求最大匹配。再和 原答案+k 取min。交了一發過了。這題網路流也可以做。待補待補。

AC程式碼:

#include <bits/stdc++.h>
#define int long long
using namespace std;
/* ***************************************************
二分圖匹配(匈牙利演算法的DFS實現)
INIT:G[][]兩邊定點劃分的情況
CALL:res=Hungary();輸出最大匹配數
優點:適於稠密圖,DFS找增廣路快,實現簡潔易於理解
時間複雜度:O(VE);
*****************************************************/
const int MAXN = 1010;
int uN,vN;//u,v數目
int G[MAXN][MAXN];//編號是1~n的
int linker[MAXN];
bool used[MAXN];
int mark[MAXN];
 
bool dfs(int u){
    int v;
    for(v=1;v<=vN;v++){
        if(mark[v]) continue;
        if(G[u][v]&&!used[v]){
            used[v]=true;
            if(linker[v]==-1||dfs(linker[v])){
                linker[v]=u;
                return true;
            }
        }
    }
    return false;
}
 
int Hungary()
{
    int res=0;
    int u;
    memset(linker,-1,sizeof(linker));
    for(u=1;u<=uN;u++){
        memset(used,false,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}
 
signed main(){
    int n,m,k;
    cin>>n>>m>>k;
    uN = n; vN = m;
    for(int i = 1 ; i <= n ;i ++){
        int cnt; cin>>cnt;
        for(int j = 0 ; j < cnt ; j ++){
            int x;
            cin>>x;
            G[i][x] = 1;
            G[i+n][x] = 1;
        }
    }
    int res1 = Hungary();
    uN = 2*n;
    int res2 = Hungary();
    int res = min(res2,res1+k);
    cout<<res<<endl;
 
    return 0;
}

J. Prime Game

題意:一個數組,求出所有子區間的數連乘之後的不同質因子的個數之和。

思路:對每個數進行質因子分解。然後計算每個質因子對最後和的貢獻。考慮一個質因子同時出現在了 a[i] 和 a[j],那麼在計算a[j]的時候,所有包含i的子區間貢獻在計算i的時候就已經計算過了。所有那麼新增的貢獻,就是經過j,且不經過i的子區間的個數。那麼也就是 (j-i)*(n-j+1)。如下:

陣列:[2,3,5,3,7]

對於第一個3,所有有貢獻的子區間是 [1,2],[1,3],[1,4],[1,5] , [2,2],[2,3],[2,4],[2,5]

對於第二個3,所有有貢獻的子區間是[3,4],[3,5],[4,4],[4,5]

還有一個需要注意的地方就是質因子處理到 sqrt(n)就行了。

AC程式碼:

#include <bits/stdc++.h>
#define ll long long
//#define int long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
const int MX  = 1e6+7;
int prime[MX];
bool vis[MX];
int tot = 0;
void pre()
{
    for(ll i = 2; i*i < MX; ++i) {
        if(!vis[i])
            prime[tot++] = i;
        for(ll j = 0; j < tot && i * prime[j] < MX; ++j) {
            vis[i*prime[j]] = 1;
            if(i%prime[j] == 0)
                break;
        }
    }
}
ll last[MX];
signed main(){
    pre();
    ll n;
    while(cin>>n) {
        ll ans = 0;
        int tmp;
        for(ll i = 1; i <= n; ++i) {
            scanf("%d", &tmp);
            for(ll j = 0; j < tot && prime[j]*prime[j] <= tmp; ++j) {
                if(tmp%prime[j] == 0) {
                    while(tmp%prime[j] == 0) {
                        tmp /= prime[j];
                    }
                    ans = ans+((i-last[prime[j]])*(n-i+1));
                    last[prime[j]] = i;
                }
            }
            if(tmp > 1){
                //cout<<tmp<<endl;
                ans = ans+((i-last[tmp])*(n-i+1));
                last[tmp] = i;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

K. Kangaroo Puzzle

題意:一個地圖。1的地方都是袋鼠。0都是牆。碰到障礙不能走。袋鼠可以疊在一起。可以用 UDLR 控制所有袋鼠同時移動。要在1e5次操作之內把他們全部移動到一個點。

思路:每次選擇兩個點。讓其中一個去追另一個。直到最後只剩下1個。這樣可以保證會走到一起。因為地圖太小。操作次數也是很有限的。不會超。有點像CF div2 DE難度的樣子。至於一個點追另一個點,就暴力模擬。先找到一條路徑。然後放到佇列裡。然後開始追。一邊進一邊出。前面的總會碰到牆的。因為保證沒有環。所以可以追到他無路可走。

AC程式碼:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+7;
int n,m;
int mp[50][50];
int dirr[][2] = {0,1,1,0,0,-1,-1,0};
char dirrr[] = {'R','D','L','U'};
int mark[50][50];
map<char,int> id;
string res = "";


void show(){
    return;
    for(int i = 0 ; i < n ; i ++){
        for(int j = 0 ; j < m ; j ++){
            cout<<mp[i][j];
        }
        cout<<endl;
    }
}

bool check(){
    int res = 0;
    for(int i = 0 ; i < n ; i ++){
        for(int j = 0 ; j < m ; j ++){
            res += mp[i][j] == 1;
            if(res == 2) return false;
        }
    }
    return true;
}

void move_kangaroo(char dir){
    if(dir == 'U'){
        for(int i = 1 ; i < n ; i ++){
            for(int j = 0 ; j < m ; j ++){
                if(mp[i][j] == 1 && mp[i-1][j] != 0){
                    mp[i-1][j] = 1;
                    mp[i][j] = 2;
                }
            }
        }
    }
    if(dir == 'D'){
        for(int i = n-2 ; i >= 0 ; i --){
            for(int j = 0 ; j < m ; j ++){
                if(mp[i][j] == 1 && mp[i+1][j] != 0){
                    mp[i+1][j] = 1;
                    mp[i][j] = 2;
                }
            }
        }
    }
    if(dir == 'L'){
        for(int i = 0 ; i < n ; i ++){
            for(int j = 1 ; j < m ; j ++){
                if(mp[i][j] == 1 && mp[i][j-1] != 0){
                    mp[i][j-1] = 1;
                    mp[i][j] = 2;
                }
            }
        }
    }
    if(dir == 'R'){
        for(int i = 0 ; i < n ; i ++){
            for(int j = m-2 ; j >= 0 ; j --){
                if(mp[i][j] == 1 && mp[i][j+1] != 0){
                    mp[i][j+1] = 1;
                    mp[i][j] = 2;
                }
            }
        }
    }
}

string dfs(int x1,int y1,int x2,int y2,string dir){
    if(x1 == x2 && y1 == y2) return dir;
    //cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<endl;
    mark[x1][y1] = 1;
    for(int i = 0 ; i < 4 ; i ++){
        int x = x1+dirr[i][0];
        int y = y1+dirr[i][1];
        if( x >= 0 && x < n && y < m && y >= 0 && !mark[x][y] && mp[x][y]){
            string dir2 = "";
            dir2 += dirrr[i];
            string tmp = dfs(x,y,x2,y2,dir2);
            if(tmp != ""){
                mark[x1][y1] = 0;
                return dir+tmp;
            }
        }
    }
    mark[x1][y1] = 0;
    string tmp = "";
    return tmp;
}

void solve(){
    int x1,x2,y1,y2;
    while(!check()){
        x1 = x2 = -1;
        for(int i = 0 ; i < n ; i ++){
            for(int j = 0 ; j < m ; j ++){
                if(mp[i][j] == 1 && x1 == -1){
                    x1 = i;
                    y1 = j;
                }else if(mp[i][j] == 1){
                    x2 = i;
                    y2 = j;
                }
            }
        }
        string tmp = "";
        string path = dfs(x1,y1,x2,y2,tmp);
        show();
        queue<char> que;
        for(int i = 0 ; i < path.size() ; i ++){
            que.push(path[i]);
        }
        while(que.size()){
            char now = que.front();que.pop();
            int tox2 = x2+dirr[id[now]][0];
            int toy2 = y2+dirr[id[now]][1];
            if(tox2 >= 0 && tox2 < n && toy2 >=0 && toy2 < m && mp[tox2][toy2]){
                x2 = tox2;
                y2 = toy2;
                que.push(now);
            }
            x1 = x1+dirr[id[now]][0];
            y1 = y1+dirr[id[now]][1];
            move_kangaroo(now);
            res += now;
            if(x1 == x2 && y1 == y2) break;
            show();
        }
    }
    show();
}




signed main(){
    id['R'] = 0;
    id['D'] = 1;
    id['L'] = 2;
    id['U'] = 3;
    cin>>n>>m;
    for(int i = 0 ; i < n ; i ++){
        string s;
        cin>>s;
        for(int j = 0 ; j < m ; j ++){
            mp[i][j] = s[j] - '0';
        }
    }
    solve();
    cout<<res<<endl;
    return 0;
}

這程式碼也能過。真 · 玄學。

#include<bits/stdc++.h>
using namespace std;
int n,m;
char tmp[23],d[4]={'L','R','U','D'};
int main()
{
    srand(6510561);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",tmp);
    for(int i=1;i<=50000;i++) printf("%c",d[rand()%4]);
}

總結:四個小時出5題。打的還不錯了。不過沒仔細檢查就交。罰時太爆炸了。隊伍溝通還是要多一些。都在各寫各的。還是浪費了挺多時間。還有就是這場題解法太多了。要繼續嘗試用所有這些可行方法再AC一遍。