ICPC中等思维题——补题(更新中)

Thinking

2020 南京 H. Harmonious Rectangle

  • Harmonious Rectangle

  • 思路意识到,当矩形长边大于 9 时,0,1,2 三个数字形成的 00,01,02,10,11,12,20,21,22 一定会出现重复出现,又因为 00,11,22 在前 7 列出现一个后,下一个一出现也会一定有重复。所以当矩形长边大于 7 时一定有解。

    小数据暴力搜索后打表,大数据直接快速幂计算即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define PII pair<int, int>
    #define Pi 3.141592653589793
    const int INF = 0x7fffffff;
    const int N = 1e5 + 5;
    const db eps = 1e-10;
    const int mod = 1e9 + 7;
    using namespace std;
    int cas, n, m, a[10][10], ok;
    ll f[10][10], res;
    int power(ll a, ll b, ll mod){  //a 为对应二进制位数需要记录的值,二进制满足即乘
        ll ans = 1;
        if(mod == 1) ans = 0;
        while(b > 0){  //快速幂即转为二进制按位计算
            if(b % 2 == 1){  //x & 1表示: x % 2 == 1
                ans *= a;
                ans %= mod;
            }
            a *= a;
            a %= mod;
            b >>= 1;
        }
        return ans;
    }
    // inline bool check(int n, int m){  //暴力计算后,得到结果,直接打表,此处为暴力计算代码
    //     rep(i, 1, n - 1) rep(j, 1, m - 1)
    //         if((a[i][j] == a[n][j] && a[i][m] == a[n][m]) || (a[i][j] == a[i][m] && a[n][j] == a[n][m])) return 1;
    //     return 0;
    // }
    // void solve(ll nowx, ll nowy, ll n, ll m){
    //     rep(color, 1, 3){
    //         a[nowx][nowy] = color, ok = 0;
    //         if(check(nowx, nowy)){
    //             ok = 1;
    //             res += power(3, (m * (n - nowx) + m - nowy), mod);
    //             res %= mod;
    //         }
    //         int tmpx = nowx, tmpy = nowy;
    //         if(!ok){  //若当前已填数字不能满足条件,才向下一个位置填数
    //             if(tmpy == m && tmpx == n) continue;
    //             if(tmpy == m) tmpx++, tmpy = 1; 
    //             else tmpy++;
    //             solve(tmpx, tmpy, n, m);
    //         }
    //     }
    // }
    int form[8][8] = {{0, 0, 0, 0, 0, 0, 0, 0}, 
        {0, 0, 0, 0, 0, 0, 0, 0}, 
        {0, 0, 15, 339, 4761, 52929, 517761, 4767849}, 
        {0, 0, 339, 16485, 518265, 14321907, 387406809, 460338013}, 
        {0, 0, 4761, 518265, 43022385, 486780060, 429534507, 792294829},       
        {0, 0, 52929, 14321907, 486780060, 288599194, 130653412, 748778899},   
        {0, 0, 517761, 387406809, 429534507, 130653412, 246336683, 579440654}, 
        {0, 0, 4767849, 460338013, 792294829, 748778899, 579440654, 236701429}
    };
    int main(){
        // rep(i, 1, 7){
        //     rep(j, 1, 7){
        //         if(i == 1 || j == 1) f[i][j] = 0;
        //         else if(j < i) f[i][j] = f[j][i];
        //         else{
        //             res = 0;
        //             memset(a, 0, sizeof(a));
        //             solve(1, 1, i, j);
        //             f[i][j] = res;
        //         }
        //     }
        // }
        cin >> cas;
        while(cas--){
            cin >> n >> m;
            if(min(n, m) == 1) cout << 0 << endl;
            //7 = 3 * 3 - 2
            else if(max(n, m) > 7)  //只有大于7才一定会重复,等于7不一定重复
                cout << power(3, n * m, mod) << endl;
            else cout << form[n][m] << endl;
        }
    }
    /*
    3
    1 4
    2 2
    3 3
    */
    

2021 昆明 Parallel Sort

  • Parallel Sort

  • 思路:由于原数列是 1 ∼ n 1\sim n 1n 的一个排列,所以没有重复数字,每个数值最终也会与位置编号一一对应。当 s z sz sz 个数字构成一个位置对应的环时,发现一种最多两步便可使所有数值归位的方法。例如:(其中第一步建成逆序排列)

    位置编号12345
    初始值51234
    第一步后54321
    第二步后12345
    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    #define Pi 3.141592653589793
    const int INF = 0x7fffffff;
    const int N = 1e5 + 5;
    const db eps = 1e-10;
    using namespace std;
    int n, a[N], circnt = 0, vis[N], now, maxn = 0, cnt2 = 0;
    VI cir[N];
    int main(){
        ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
        cin >> n;
        rep(i, 1, n) cin >> a[i];
        memset(vis, 0, sizeof(vis));
        rep(i, 1, n){
            if(vis[i]) continue;
            now = i;  //储存位置编号
            circnt++;
            while(!vis[now]){
                cir[circnt].push_back(now);
                vis[now] = 1;
                now = a[now];  //转移到下一位置
            }
        }
        rep(i, 1, circnt){
            int sz = (int)cir[i].size();
            if(sz == 2) cnt2++;
            maxn = max(maxn, sz);
        }
        if(maxn == 1) cout << 0 << endl;
        else if(maxn == 2){
            cout << 1 << endl << cnt2 << " ";
            rep(i, 1, circnt){
                if((int)cir[i].size() == 1) continue;
                for(auto j : cir[i]) cout << j << " ";
            }
        }
        else{
            cout << 2 << endl;
            //step1: 只变 size(长度) 大于 2 的环
            int k1 = 0;
            rep(i, 1, circnt){
                int sz = (int)cir[i].size();
                if(sz == 1 || sz == 2) continue;
                k1 += (sz - (sz % 2 ? 0 : 2)) / 2;
            }
            cout << k1 << " ";
            rep(i, 1, circnt){
                int sz = (int)cir[i].size();
                if(sz == 1 || sz == 2) continue;
                rep(j, 1, sz / 2 - (sz % 2 ? 0 : 1)){  //输出加交换
                    cout << cir[i][j] << " " << cir[i][sz - j] << " ";
                    swap(cir[i][j], cir[i][sz - j]);
                }
            }
            cout << endl;
            //step2: 变所有 size(长度) 大于 1 的环
            int k2 = 0;
            rep(i, 1, circnt) k2 += (int)cir[i].size() / 2;
            cout << k2 << " ";
            rep(i, 1, circnt){
                int sz = (int)cir[i].size();
                if(sz == 1) continue;
                rep(j, 0, sz / 2 - 1)  //直接输出即可
                    cout << cir[i][j] << " " << cir[i][sz - 1 - j] << " ";
            }
        }
    }
    

2019 哈尔滨 I. Interesting Permutation

  • Interesting Permutation

  • 思路 O ( n ) O(n) O(n)
    ∵ f i = max ⁡ { a i , f i − 1 } , g i = min ⁡ { a i , g i − 1 } ∴ h i = max ⁡ { a i , f i − 1 } − min ⁡ { a i , g i − 1 } 若 a i > f i , h i = a i − g i − 1 > h i − 1 若 g i − 1 < a i < f i , h i = f i − 1 − g i − 1 = h i − 1 若 a i < g i − 1 , h i = f i − 1 − a i > h i − 1 \because f_i=\max\{a_i,f_{i-1}\},g_i=\min\{a_i,g_{i-1}\} \qquad\therefore h_i=\max\{a_i,f_{i-1}\}-\min\{a_i,g_{i-1}\}\\ \begin{aligned}&若a_i>f_i,&h_i=a_i-g_{i-1}>h_{i-1}\\ &若g_{i-1}<a_i<f_i,&h_i=f_{i-1}-g_{i-1}=h_{i-1}\\ &若a_i<g_{i-1},&h_i=f_{i-1}-a_i>h_{i-1}\\ \end{aligned} fi=max{ai,fi1},gi=min{ai,gi1}hi=max{ai,fi1}min{ai,gi1}ai>fi,gi1<ai<fi,ai<gi1,hi=aigi1>hi1hi=fi1gi1=hi1hi=fi1ai>hi1

    • 因此 h 1 = 0 , h i ≥ h i − 1 h_1=0,h_i\ge h_{i-1} h1=0,hihi1 一定成立,于是无解情况需要充分讨论。

    • 有解时

      • h i > h i − 1 h_i>h_{i-1} hi>hi1,那么 a i a_i ai 可以为最大值或最小值两种情况,同时 g i − 1 ∼ f i − 1 g_{i-1}\sim f_{i-1} gi1fi1 变为 g i ∼ f i g_i\sim f_i gifi ,其中增加 h i − h i − 1 − 1 h_i-h_{i-1}-1 hihi11 个空位。
      • h i = h i − 1 h_i=h_{i-1} hi=hi1,那么 a i a_i ai 只能在 g i − 1 ∼ f i − 1 g_{i-1}\sim f_{i-1} gi1fi1 中的空位中选择一个,并用掉一个空位。
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    #define Pi 3.141592653589793
    const int INF = 0x7fffffff;
    const int N = 2e6 + 200;
    const db eps = 1e-10;
    const ll mod = 1e9 + 7;
    int cas, n, ok;
    ll ans, h[N], midempty;
    int main(){
        ios::sync_with_stdio(0),cin.tie(0);
        cin >> cas;
        while(cas--){
            cin >> n;
            ok = 1;
            rep(i, 1, n){
                cin >> h[i];
                //无解情况
                if(i == 1 && h[i] != 0) ok = 0;
                if(i > 1 && h[i] == 0) ok = 0;  //a 为一个排列
                if(h[i] >= n) ok = 0;
                if(h[i - 1] > h[i]) ok = 0;
            }
            if(!ok){
                cout << 0 << endl;   //不要轻易用puts("0") 不知道为什么会错!!!
                continue;
            }
            ans = 1, midempty = 0;
            rep(i, 2, n){
                if(h[i] == h[i - 1]){
                    (ans *= midempty) %= mod;
                    midempty--;
                }
                else{ // h[i] > h[i - 1]
                    (ans *= 2) %= mod;
                    midempty += h[i] - h[i - 1] - 1;
                }
            }
            cout << ans << endl;
        }
    }
    /*
    3
    3
    0 2 2
    3
    0 1 2
    3
    0 2 3
    */
    

2020 上海 D. Walker

  • Walker

  • 思路

    • 首先 calc 这个函数绝妙,省去许多情况的讨论。

    • 其次,分为三种情况:

      1. 一个人速度特别快,自己就走完全程即可。
      2. 两个人速度差距不大,但是速度慢的离自己临近端点较远,两个人只需要走到对面端点即可。
      3. 两个人分别负责自己的区间,争取同时完成(但不能强求),两个区间交点便应该在 p1p2 之间,二分查找最优答案即可。

      我们一开始想到二分,但是写的复杂了许多,还没有用那个函数,越写越臃肿,思路就更乱了。

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 1e5 + 5;
    const db eps = 1e-10;
    int cas;
    db p1, p2, v1, v2, n, ans;
    db calc(db l, db r, db v){  //左边距离为l,右边距离为r时,速度为v所需要的时间
        return (min(l, r) * 2.0 + max(l, r)) / v;
    }
    int main(){
        cin >> cas;
        while(cas--){
            scanf("%lf%lf%lf%lf%lf", &n, &p1, &v1, &p2, &v2);
            if(p1 > p2){
                swap(p1, p2);
                swap(v1, v2);
            }
            //只有一个走
            ans = min(calc(p1, n - p1, v1), calc(p2, n - p2, v2));
            //交叉走
            ans = min(ans, max((n - p1) / v1, p2 / v2));
            //两人各走左右两边
            db l = p1, r = p2, mid;
            while(r - l > eps){
                mid = (l + r) / 2;
                if(calc(p1, mid - p1, v1) > calc(p2 - mid, n - p2, v2)) r = mid;
                else l = mid;
            }
            ans = min(ans, max(calc(p1, l - p1, v1), calc(p2 - l, n - p2, v2)));
            printf("%.9lf\n", ans);
        }
    }
    /*
    2
    10000.0 1.0 0.001 9999.0 0.001
    4306.063 4079.874 0.607 1033.423 0.847
    */
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值