HZNU Training 26 for Zhejiang Provincial Competition 2020

POJ 3076 Sudoku
思路:
dfs + 剪枝
首先,如果这个位置只能填一种字母,那就直接填
其次,如果对于每一种字母,如果某一列或者某一行或者某一块只能填它,那就填它
然后,对于某个位置如果不能填字母了,或者某种字母在一行一列或一块中出向了两次以上,说明当前方案不成立
最后贪心地从可选情况少的往下搜

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<string>
#include<vector>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<string.h>
#define L(x) (1 << (x))
using namespace std;
typedef long long ll;
#define lson l , mid , rt << 1
#define rson mid + 1 , read , rt << 1 | 1

ll read()
{
    ll x = 0, f = 1; char ch = getchar();
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

const int maxn = 20;
int mp[maxn][maxn];
int st[maxn][maxn];
int sum = 0;
char s[maxn][maxn + 10];
void add(int x, int y, int t) {
    mp[x][y] = t;
    sum++;
    for (int i = 1; i <= 16; i++) {
        st[i][y] |= 1 << t - 1;
        st[x][i] |= 1 << t - 1;
    }
    int xx = (x + 3) / 4, yy = (y + 3) / 4;
    for (int i = (xx - 1) * 4 + 1; i <= xx * 4; i++) {
        for (int j = (yy - 1) * 4 + 1; j <= yy * 4; j++) {
            st[i][j] |= 1 << t - 1;
        }
    }
}
void print() {
    for (int i = 1; i <= 16; i++) {
        for (int j = 1; j <= 16; j++) {
            putchar(mp[i][j] - 1 + 'A');
        }
        puts("");
    }
    puts("");
}
bool dfs() {
    if (sum == 256) {
        print();
        return true;
    }

    for (int i = 1; i <= 16; i++) {
        for (int j = 1; j <= 16; j++) {
            if (!mp[i][j]) {
                int cnt = 0, t = 0;
                for (int k = 1; k <= 16; k++) {
                    if ((st[i][j] & (1 << k - 1)) == 0) {
                        cnt++;
                        t = k;
                        if (cnt == 2) break;
                    }
                }
                if (!cnt) return false;
                if (cnt == 1) add(i, j, t);
            }
        }
    }

    for (int i = 1; i <= 16; i++) {
        for (int k = 1; k <= 16; k++) {
            int cnt1 = 0, cnt2 = 0, y;
            for (int j = 1; j <= 16; j++) {
                if (mp[i][j] == k) cnt1++;
                if (cnt1 == 2) return false;
                if (!mp[i][j] && (st[i][j] & (1 << k - 1)) == 0) cnt2++, y = j;
            }
            if (!cnt1 && !cnt2) return false;
            if (!cnt1 && cnt2 == 1) add(i, y, k);
        }
    }

    for (int j = 1; j <= 16; j++) {
        for (int k = 1; k <= 16; k++) {
            int cnt1 = 0, cnt2 = 0, x;
            for (int i = 1; i <= 16; i++) {
                if (mp[i][j] == k) cnt1++;
                if (cnt1 == 2) return false;
                if (!mp[i][j] && (st[i][j] & (1 << k - 1)) == 0) cnt2++, x = i;
            }
            if (!cnt1 && !cnt2) return false;
            if (!cnt1 && cnt2 == 1) add(x, j, k);
        }
    }

    for (int i = 1; i <= 16; i++) {
        int x = (i + 3) / 4, y = i - (x - 1) * 4;
        for (int k = 1; k <= 16; k++) {
            int cnt1 = 0, cnt2 = 0, xx, yy;
            for (int ii = (x - 1) * 4 + 1; ii <= x * 4; ii++) {
                for (int jj = (y - 1) * 4 + 1; jj <= y * 4; jj++) {
                    if (mp[ii][jj] == k) cnt1++;
                    if (cnt1 == 2) return false;
                    if (!mp[ii][jj] && (st[ii][jj] & (1 << k - 1)) == 0) cnt2++, xx = ii, yy = jj;
                }
            }
            if (!cnt1 && !cnt2) return false;
            if (!cnt1 && cnt2 == 1) add(xx, yy, k);
        }
    }
    if (sum == 256) {
        print();
        return true;
    }

    int mn = maxn, x, y;
    for (int i = 1; i <= 16; i++) {
        for (int j = 1; j <= 16; j++) {
            if (!mp[i][j]) {
                int cnt = 0;
                for (int k = 1; k <= 16; k++) {
                    if ((st[i][j] & (1 << k - 1)) == 0) {
                        cnt++;
                        if (cnt >= mn) break;
                    }
                }
                if (cnt < mn) {
                    mn = cnt;
                    x = i;
                    y = j;
                }
            }
        }
    }
    int tst[maxn][maxn], tmp[maxn][maxn];
    memcpy(tst, st, sizeof(st));
    memcpy(tmp, mp, sizeof(mp));
    int tsum = sum;

    for (int k = 1; k <= 16; k++) {
        if ((st[x][y] & (1 << k - 1)) == 0) {
            add(x, y, k);
            bool f = dfs();
            if (!f) {
                memcpy(st, tst, sizeof(tst));
                memcpy(mp, tmp, sizeof(tmp));
                sum = tsum;
            }
            else return true;
        }
    }
    return false;
}
int main() {
    while (scanf("%s", s[1] + 1) != EOF) {
        for (int i = 2; i <= 16; i++) {
            scanf("%s", s[i] + 1);
        }
        sum = 0;
        memset(mp, 0, sizeof mp);
        memset(st, 0, sizeof st);
        for (int i = 1; i <= 16; i++) {
            for (int j = 1; j <= 16; j++) {
                if (isalpha(s[i][j])) add(i, j, s[i][j] - 'A' + 1);
            }
        }
        dfs();
    }
    return 0;
}
View Code

POJ 1741 Tree
题目大意
多组测试数据,每次输入n、m,和一棵n个点的有边权的树,问你满足x到y距离小于等于m的无序点对(x,y)的个数是多少。
题解
树的点分治模板题
考虑到路径只有两种情况,一是经过根节点,二是不经过根节点。
如果不经过根节点,那么一定经过最小公共子树的根节点,可以转化为问题一的子问题。
于是考虑怎么递归解决问题一。
对于根节点进行一次dfs,求出deep,并将其从小到大排序。
避免重复,只需要求出其中deep[x]≤deep[y]且deep[x]+deep[y]≤m的个数。
用i表示左指针,j表示右指针,i从左向右遍历。
如果deep[i]+deep[j]≤m,则点对(i,t)(i<t≤j)都符合题意,将j-i加入答案中,并且i++;否则j--。
然而这样还会重复计算在同一棵子树中的点对,所以再进行下一步dfs之前需要减去重复部分。
因为树可能会退化,导致选择链头时时间复杂度极大而TLE。
于是每次不能固定选择root,而是以重心作为root去处理,这样能保证时间复杂度再O(nlog2n)以下。

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<string>
#include<vector>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<string.h>
#define L(x) (1 << (x))
using namespace std;
typedef long long ll;
#define lson l , mid , rt << 1
#define rson mid + 1 , read , rt << 1 | 1

ll read()
{
    ll x = 0, f = 1; char ch = getchar();
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

const int maxn = 10010;
int m;
int head[maxn], to[maxn << 1], len[maxn << 1], net[maxn << 1];
int cnt, si[maxn], deep[maxn], root, vis[maxn], f[maxn], sn, d[maxn];
int tot, ans;
void add(int x, int y, int z)
{
    to[++cnt] = y, len[cnt] = z, net[cnt] = head[x], head[x] = cnt;
}
void getroot(int x, int fa)
{
    f[x] = 0, si[x] = 1;
    int i;
    for (i = head[x]; i; i = net[i])
        if (to[i] != fa && !vis[to[i]])
            getroot(to[i], x), si[x] += si[to[i]], f[x] = max(f[x], si[to[i]]);
    f[x] = max(f[x], sn - si[x]);
    if (f[root] > f[x]) root = x;
}
void getdeep(int x, int fa)
{
    d[++tot] = deep[x];
    int i;
    for (i = head[x]; i; i = net[i])
        if (to[i] != fa && !vis[to[i]])
            deep[to[i]] = deep[x] + len[i], getdeep(to[i], x);
}
int calc(int x)
{
    tot = 0, getdeep(x, 0), sort(d + 1, d + tot + 1);
    int i = 1, j = tot, sum = 0;
    while (i < j)
    {
        if (d[i] + d[j] <= m) sum += j - i, i++;
        else j--;
    }
    return sum;
}
void dfs(int x)
{
    deep[x] = 0, vis[x] = 1, ans += calc(x);
    int i;
    for (i = head[x]; i; i = net[i])
        if (!vis[to[i]])
            deep[to[i]] = len[i], ans -= calc(to[i]), sn = si[to[i]], root = 0, getroot(to[i], 0), dfs(root);
}
int main()
{
    int n, i, x, y, z;
    while (scanf("%d%d", &n, &m) && (n || m))
    {
        memset(head, 0, sizeof(head));
        memset(vis, 0, sizeof(vis));
        cnt = 0, ans = 0;
        for (i = 1; i < n; i++)
            scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z);
        f[0] = 0x7fffffff, sn = n;
        root = 0, getroot(1, 0), dfs(root);
        printf("%d\n", ans);
    }
    return 0;
}
View Code

POJ 1852 Ants
题目大意
在一根长为L的水平木棍上有一群数量为n的蚂蚁,它们以每秒1cm/s的速度走到木棍一端就会掉下去。
现在知道它们的起始位置是距离木根左端点的x处。但是不知道它们爬行的方向。
在相向而行的两只蚂蚁相遇后,它们会掉头往反方向走。问所有蚂蚁都落下木棍的最快时间和最慢时间。
题解
因为是同时出发的,相遇时的两只蚂蚁用的时间是相同的,我们可以无视蚂蚁的区别,当两只蚂蚁相遇时它们保持原样交错而行。
这样每只蚂蚁都是独立运动的,那么只要找每只蚂蚁掉下去的时间就行了。

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<string>
#include<vector>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<string.h>
#define L(x) (1 << (x))
using namespace std;
typedef long long ll;
#define lson l , mid , rt << 1
#define rson mid + 1 , read , rt << 1 | 1

ll read()
{
    ll x = 0, f = 1; char ch = getchar();
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

const int maxn = 1000010;
int a[maxn];
int ansmin, ansmax, L, n;
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &L, &n);
        for (int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
        }

        int minn;
        ansmin = -1;
        for (int i = 0; i < n; ++i)
        {
            minn = min(a[i], L - a[i]);
            if (minn > ansmin)
                ansmin = minn;
        }
        printf("%d ", ansmin);

        int maxx;
        ansmax = -1;
        for (int i = 0; i < n; ++i)
        {
            maxx = max(a[i], L - a[i]);
            if (maxx > ansmax)
                ansmax = maxx;
        }
        printf("%d\n", ansmax);
    }
    return 0;
}
View Code

POJ 2407 Relatives
裸的欧拉函数,直接用公式

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<string>
#include<vector>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<string.h>
#define L(x) (1 << (x))
using namespace std;
typedef long long ll;
#define lson l , mid , rt << 1
#define rson mid + 1 , read , rt << 1 | 1

ll read()
{
    ll x = 0, f = 1; char ch = getchar();
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int Q(int n)
{
    int ans = n;
    for (int i = 2; i <= n; i++){
        if (n%i == 0){
            ans -= ans / i;
            while (n%i == 0) {
                n /= i;
            }
        }
        if (n == 1) break;
    }
    return ans;
}

int main()
{
    int n;
    while (cin >> n && n){
        cout << Q(n) << endl;
    }
    return 0;
}
View Code

 

posted @ 2020-04-28 21:18  Fzzf1  阅读(171)  评论(0编辑  收藏  举报