2021.02.18 北师大寒假新生训练

2021.02.18 北师大寒假新生训练

Candies and Two Sisters

  • 签到
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main() {
    
    
	int T;
	scanf("%d", &T);
	while (T--) {
    
    
		int n;
		scanf("%d", &n);
		if (n & 1) printf("%d\n", n / 2);
		else printf("%d\n", n / 2 - 1);
	}
	return 0;
}

Construct the String

  • 从’a’ 到 ‘a’ + b 循环往里面填字母就好。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 2010;

char s[maxn];

int main() {
    
    
	int T;
	scanf("%d", &T);
	while (T--) {
    
    
		int n, a, b;
		scanf("%d%d%d", &n, &a, &b);
		for (int i = 0; i < n; i++) {
    
    
			s[i] = 'a' + i % b;
		}
		s[n] = 0;
		printf("%s\n", s);
	}
	return 0;
}

Two Teams Composing

  • 分类讨论题
    { t y p e , t y p e ≤ m − 1 ; m − 1 , t y p e = m ; m , t y p e > m . \begin{cases}type, \quad type \le m-1; \\ m-1, \quad type = m; \\ m, \quad type > m.\end{cases} type,typem1;m1,type=m;m,type>m.
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
using namespace std;

unordered_map<int, int> um;
int main() {
    
    
	int T;
	scanf("%d", &T);
	while (T--) {
    
    
		int n;
		scanf("%d", &n);
		um.clear();
		int m = 0;
		for (int i = 0; i < n; i++) {
    
    
			int x;
			scanf("%d", &x);
			um[x]++;
			m = max(um[x], m);
		}
		int type = um.size();
		if (type <= m - 1) printf("%d\n", type);
		else if (type == m) printf("%d\n", m - 1);
		else printf("%d\n", m);
	}
	return 0;
}

Anti-Sudoku

在这里插入图片描述

  • 依次挑选小矩阵中的元素,比如挑选 第(i,j) 的小矩形,那么就挑选小矩形中的 (j,i) 元素,只要修改的和之前的不一样,那么一定就会满足条件,因为该行该列该小矩阵都是只有这一个元素被修改掉。
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

char mz[15][15];

int main() {
    
    
	int T;
	scanf("%d", &T);
	while (T--) {
    
    
		for (int i = 1; i <= 9; i++) scanf("%s", mz[i] + 1);
		for (int i = 1; i <= 3; i++) {
    
    
			for (int j = 1; j <= 3; j++) {
    
    
				int x = 3 * (j - 1) + i, y = 3 * (i - 1) + j;
				if (mz[x][y] == '1') mz[x][y] = '2';
				else mz[x][y] = '1';
			}
		}
		for (int i = 1; i <= 9; i++) printf("%s\n", mz[i] + 1);
	}
	return 0;
}

Three Blocks Palindrome (hard version)

  • 我们发现a的范围很小,于是我们可以先确定第一个数字,然后用双指针确定x的值,那么中间y的值就是两边对称的x,所夹区间出现次数最多的数字是多少。
  • 那么难点就在于,我们能在 O ( 1 ) O(1) O(1) 求数组中某个区间,某个数字出现的次数。
  • 其实可以用前缀和,开一个数组 a [ 200010 ] [ 210 ] a[200010][210] a[200010][210], a [ i ] [ j ] a[i][j] a[i][j] 记录数字 j j j 在第 i 个位置是否出现。出现的话为1,没有出现为0。然后求这个数字的前缀和。这样子,就解决了上述那个问题。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

const int maxn = 200010, maxa = 210;
int a[maxn][maxa];
vector<int> ids[maxa];

int main() {
    
    
	int T;
	scanf("%d", &T);
	while (T--) {
    
    
		int N;
		scanf("%d", &N);

		for (int i = 1; i <= 200; i++) ids[i].clear();

		for (int i = 1; i <= N; i++) {
    
    
			memset(a[i], 0, sizeof a[i]);
			int x;
			scanf("%d", &x);
			a[i][x] = 1;
			ids[x].push_back(i);
		}

		int maxsz = 0;

		for (int j = 1; j <= 200; j++) {
    
    
			maxsz = max(maxsz, (int)ids[j].size());
			for (int i = 1; i <= N; i++) {
    
    
				a[i][j] += a[i - 1][j];
			}
		}

		int ans = maxsz;
		for (int m = 1; m <= 200; m++) {
    
    
			if ((int)ids[m].size() <= 1) continue;
			int sz = (int)ids[m].size();
			int l = 0, r = sz - 1;
			while (r > l) {
    
    
				int x = l + 1;
				int y = 0;
				for (int i = 1; i <= 200; i++) {
    
    
					y = max(y, a[ids[m][r] - 1][i] - a[ids[m][l]][i]);
					//if (i == 1) printf("### %d\n", a[ids[m][r] - 1][i] - a[ids[m][l]][i]);
				}

				ans = max(2 * x + y, ans);
				l++, r--;
			}
		}
		printf("%d\n", ans);
	}
}

Robots on a Grid

  • 图论(队友写的)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
string dir[1000010],color[1000010];
int occupy[1000010];
int p[1000010][30],ans1,ans2;
void init()
{
    
    
    ans1=ans2=0;
    for(int i=1;i<=n;i++)
    {
    
    
        for(int j=1;j<=m;j++)
        {
    
    
            int x=(i-1)*m+j;
            occupy[x]=-1;
            for(int k=0;k<=25;k++)
                p[x][k]=0;
        }
    }
    for(int i=1;i<=n;i++)
    {
    
    
        for(int j=0;j<m;j++)
        {
    
    
            int u=(i-1)*m+j+1;
            if(dir[i][j]=='U')
            {
    
    
                p[u][0]=u-m;
            }
            else if(dir[i][j]=='D')
            {
    
    
                p[u][0]=u+m;
            }
            else if(dir[i][j]=='L')
            {
    
    
                p[u][0]=u-1;
            }
            else if(dir[i][j]=='R')
            {
    
    
                p[u][0]=u+1;
            }
        }
    }
    for(int j=1;(1<<j)<=n*m;j++)
    {
    
    
        for(int i=1;i<=n*m;i++)
        {
    
    
            p[i][j]=p[p[i][j-1]][j-1];
        }
    }
}
void solve()
{
    
    
    for(int i=1;i<=n;i++)
    {
    
    
        for(int j=0;j<m;j++)
        {
    
    
            int c=color[i][j]-'0',u=(i-1)*m+j+1;
            for(int k=(int)log2(n*m);k>=0;k--)
            {
    
    
                if(!p[u][k])
                    continue;
                u=p[u][k];
            }
            if(occupy[u]!=0)
                occupy[u]=c;
        }
    }
    for(int i=1;i<=n*m;i++)
    {
    
    
        if(occupy[i]!=-1)
            ans1++;
        if(occupy[i]==0)
            ans2++;
    }
}
int main()
{
    
    
    int T;
    scanf("%d",&T);
    while(T--)
    {
    
    
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
    
    
            cin>>color[i];
        }
        for(int i=1;i<=n;i++)
        {
    
    
            cin>>dir[i];
        }
        init();
        solve();
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}

Decimal

  • 找找规律会发现,把n质因数分解后,只要分解的结果,含有除2或5以外其他质因数,那么就是无限小数。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main() {
    
    
	int T;
	cin >> T;
	while (T--) {
    
    
		int n;
		cin >> n;
		while (n % 2 == 0) n /= 2;
		while (n % 5 == 0) n /= 5;
		if (n == 1) printf("No\n");
		else printf("Yes\n");
	}
	return 0;
}

Forest Program

  • 图论
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll vis[300010],pre[300010],sum;
ll h[1000010],ne[1000010],e[1000010],idx;
ll ans=1;
ll qmi(ll m,ll k)
{
    
    
    ll res=1,t=m;
    while(k)
    {
    
    
        if(k&1)
            res=res*t%mod;
        t=t*t%mod;
        k>>=1;
    }
    return res;
}
void add(ll a,ll b)
{
    
    
    e[++idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
}
void dfs(ll u,ll fa)
{
    
    
    vis[u]=1;
    for(ll i=h[u];i;i=ne[i])
    {
    
    
        ll v=e[i];
        if(v==fa)
            continue;
        if(vis[v]==2)
            continue;
        if(!vis[v])
        {
    
    
            pre[v]=u;
            dfs(v,u);
        }
        else
        {
    
    
            ll step=1;
            ll tmp=u;
            while(tmp!=v)
            {
    
    
                step++;
                tmp=pre[tmp];
            }
            sum+=step;
            ans=ans*(qmi(2,step)-1)%mod;
        }
    }
    vis[u]=2;
}
int main()
{
    
    
    ll n,m;
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=m;i++)
    {
    
    
        ll a,b;
        scanf("%lld%lld",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(ll i=1;i<=n;i++)
    {
    
    
        if(!vis[i])
        {
    
    
            dfs(i,0);
        }
    }
    ll res=m-sum;
    ans=ans*qmi(2,res)%mod;
    printf("%lld",ans);
    return 0;
}

Angle Beats

  • 给定一个点,问有多少条点对,可以与这个点构成一个直角三角形。
  • 思路很简单,就是非常卡时间。我们分为 A i A_i Ai 是否作为直角边分类讨论。把所有的边存在哈希表中,然后查找。
  • 省时间策略:
  1. 不用map,用unordered_map
  2. unordered_map里面不要存pair,更不要存三元组,存一维的元素,省空间省时间。具体hash的办法很简单,就是 x * hash_num + y 即可。
  3. 把线段方向保存为向量(dx, dy),而不是保存倾斜角,防止浮点数误差。dx, dy 都除以最大公约数,用这种方式把它们都标准化。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
using namespace std;

#define x first
#define y second
typedef long long ll;
const int maxn = 2010;
const ll hash_num = 3e9 + 9;
typedef pair<int, int> P;

ll Hash(const P& p) {
    
    
    return hash_num * (ll)p.x + (ll)p.y;
}



P ps[maxn], qs[maxn];
unordered_map<ll, int> tmap;
int ans[maxn];

inline int gcd(int a, int b) {
    
    
    if (b == 0) return a;
    return gcd(b, a % b);
}

inline void normalize(P& p) {
    
    
    int dx = p.x, dy = p.y;
    int d = gcd(abs(dx), abs(dy));
    dy /= d, dx /= d;
    p.x = dx, p.y = dy;
}


int main() {
    
    
    int N, Q;
    scanf("%d%d", &N, &Q);
    for (int i = 1; i <= N; i++) {
    
    
        scanf("%d%d", &ps[i].x, &ps[i].y);
    }
    for (int i = 1; i <= Q; i++) {
    
    
        scanf("%d%d", &qs[i].x, &qs[i].y);
    }
    for (int i = 1; i <= Q; i++) {
    
    
        tmap.clear();
        for (int j = 1; j <= N; j++) {
    
    
            P p(qs[i].x - ps[j].x, qs[i].y - ps[j].y);
            normalize(p);
            tmap[Hash(p)]++;
        }

        for (int j = 1; j <= N; j++) {
    
    
            P p(qs[i].x - ps[j].x, qs[i].y - ps[j].y);
            normalize(p);

            //两个方向都要加上去
            ans[i] += tmap[Hash(P(-p.y, p.x))];
            ans[i] += tmap[Hash(P(p.y, -p.x))];
        }
        ans[i] /= 2;
    }
    
    for (int i = 1; i <= N; i++) {
    
    
        tmap.clear();
        for (int j = 1; j <= N; j++) {
    
    
            if (i != j) {
    
    
                P p = {
    
     ps[i].x - ps[j].x, ps[i].y - ps[j].y };
                normalize(p);
                tmap[Hash(p)]++;
            }
        }
        for (int j = 1; j <= Q; j++) {
    
    
            P p = {
    
     ps[i].x - qs[j].x, ps[i].y - qs[j].y };
            normalize(p);
            ans[j] += tmap[Hash(P(-p.y, p.x))];
            ans[j] += tmap[Hash(P(p.y, -p.x))];
        }
    }

    for (int i = 1; i <= Q; i++) {
    
    
        printf("%d\n", ans[i]);
    }
    return 0;
}

Invoker

  • dp问题,感觉应该归为一个状态机的问题。 d p [ i ] [ j ] dp[i][j] dp[i][j] = m i n ( d p [ i ] [ j ] , d p [ i − 1 ] [ k ] + d i s t ( k → j ) min(dp[i][j],dp[i-1][k]+dist(k \rightarrow j) min(dp[i][j],dp[i1][k]+dist(kj)。搜索从 i - 1 到 i 状态,从6种字母到另外 6 种字母,的最小值。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
using namespace std;

char words[10][6][4] = {
    
     
 {
    
    "QQQ","QQQ","QQQ","QQQ","QQQ","QQQ"},
 {
    
    "QQW","QWQ","WQQ","WQQ","WQQ","WQQ"},
 {
    
    "QQE","QEQ","EQQ","EQQ","EQQ","EQQ"},
 {
    
    "WWW","WWW","WWW","WWW","WWW","WWW"},
 {
    
    "QWW","WQW","WWQ","WWQ","WWQ","WWQ"},
 {
    
    "WWE","WEW","EWW","EWW","EWW","EWW"},
 {
    
    "EEE","EEE","EEE","EEE","EEE","EEE"},
 {
    
    "QEE","EQE","EEQ","EEQ","EEQ","EEQ"},
 {
    
    "WEE","EWE","EEW","EEW","EEW","EEW"},
 {
    
    "QWE","QEW","EQW","EWQ","WEQ","WQE"},
};

const int maxn = 100010;
char str[maxn], p[maxn];
int f[maxn][6];
unordered_map<char, int> um;

int dist(int a, int b, int x, int y) {
    
    
    
    //计算从 words[a][b] 到 words[x][y] 需要在末尾添加几个字母,即用几次技能。
    if (words[a][b][0] == words[x][y][0] && words[a][b][1] == words[x][y][1] && words[a][b][2] == words[x][y][2]) {
    
    
        return 0;
    }
    if (words[a][b][1] == words[x][y][0] && words[a][b][2] == words[x][y][1]) {
    
    
        return 1;
    }
    if (words[a][b][2] == words[x][y][0]) {
    
    
        return 2;
    }
    return 3;
}


int main() {
    
    
    um['X'] = 0; um['V'] = 1; um['G'] = 2;
    um['C'] = 3; um['X'] = 4; um['Z'] = 5;
    um['T'] = 6; um['F'] = 7; um['D'] = 8; um['B'] = 9;
    scanf("%s", str);
    //前后两个字母相同只需要按R,把这些东西先挑出来。
    int sz = 0, res1 = strlen(str);
    p[sz++] = um[str[0]];
    for (int i = 1; str[i]; i++) {
    
    
        if (str[i] != str[i - 1]) p[sz++] = um[str[i]];
    }
    //for (int i = 0; i < sz; i++) {
    
    
    //    printf("* %d\n", p[i]);
    //}
    //printf("%d\n", res1);

    memset(f, 0x3f, sizeof f);

    for (int j = 0; j < 6; j++) f[0][j] = 3;

    for (int i = 1; i < sz; i++) {
    
    
        for (int j = 0; j < 6; j++) {
    
    
            for (int k = 0; k < 6; k++) {
    
    
                f[i][j] = min(f[i][j], f[i - 1][k] + dist(p[i - 1], k, p[i], j));
            }
            //printf("### %d %d %d\n", i, j, f[i][j]);
        }
       
    }
    int res2 = 1e9;
    for (int j = 0; j < 6; j++) {
    
    
        res2 = min(res2, f[sz - 1][j]);
    }
    printf("%d\n", res1 + res2);
    return 0;
}

MUV LUV EXTRA

  • 字符串,KMP + 最小循环节。我们发现一个规律(假设ne数组和模式串下标均从1开始):最小循环节(题目中的 l)= i - ne[i]. (其实告诉这个规律后,发现道理也挺显然的)
  • i 从 1 到 N 一直更新答案就可以了 ans = max(ans, a * i - b * (i - ne[i]))

完整代码:

#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 10000010;
int ne[maxn], N;
char str[maxn], p[maxn];
typedef long long ll;
ll a, b;
int main() {
    
    
	scanf("%lld%lld%s", &a, &b, str);

	for (int i = 0; str[i]; i++) {
    
    
		if (str[i] == '.') {
    
    
			strcpy(p + 1, str + i + 1);
			break;
		}
	}

	N = strlen(p + 1);
	reverse(p + 1, p + N + 1);
	//计算 ne 数组
	for (int i = 2, j = 0; i <= N; i++) {
    
    
		while (j && p[i] != p[j + 1]) j = ne[j];
		if (p[i] == p[j + 1]) j++;
		ne[i] = j;
	}
	
	//printf("### %s\n", p + 1);
	//for (int i = 1; i <= N; i++) printf("*** %d %d\n", i, ne[i]);

	ll ans = -1e9;
	for (int i = 1; i <= N; i++) {
    
    
		ll p = i, l = i - ne[i];
		ans = max(ans, a * p - b * l);
	}
	printf("%lld\n", ans);
	return 0;
}

MUV LUV UNLIMITED

  • 树上博弈
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int fa[1000010],du[1000010],n;
int h[1000010],ne[1000010],e[1000010],idx;
int depth[1000010];
void add(int a,int b)
{
    
    
    e[++idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
}
void dfs(int u,int len)
{
    
    
    if(du[u]>1)
        len=0;
    depth[u]=len;
    for(int i=h[u];i;i=ne[i])
    {
    
    
        int v=e[i];
        dfs(v,len+1);
    }
}
int main()
{
    
    
    int T;
    scanf("%d",&T);
    while(T--)
    {
    
    
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
    
    
            du[i]=0;
            h[i]=0;
            depth[i]=0;
        }
        idx=0;
        for(int i=2;i<=n;i++)
        {
    
    
            int x;
            scanf("%d",&x);
            fa[i]=x;
            du[x]++;
            add(x,i);
        }
        bool flag=false;
        for(int i=1;i<=n&&!flag;i++)
        {
    
    
            if(!du[i])
            {
    
    
                int x=fa[i];
                if(du[x]>1)
                {
    
    
                    flag=true;
                }
            }
        }
        for(int i=1;i<=n&&!flag;i++)
        {
    
    
            if(!du[i])
            {
    
    
                if(depth[i]%2)
                    flag=true;
            }
        }
        if(flag)
            printf("Takeru\n");
        else
            printf("Meiya\n");
    }
    return 0;
}

Escape

  • 分层图 & 网络流
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int n,m,rob,exc;
int h[1000010],e[1000010],ne[1000010],capacity[1000010],flow[1000010],idx;
int S,T;
char cell[110][110];
int dis[60010],curedge[1000010];
void add(int a,int b,int c)
{
    
    
    e[idx]=b;
    ne[idx]=h[a];
    capacity[idx]=c;
    flow[idx]=0;
    h[a]=idx++;
}
void init()
{
    
    
    memset(h,-1,sizeof h);
    idx=0;
}
void input()
{
    
    
    scanf("%d%d%d%d",&n,&m,&rob,&exc);
    T=n*m*6;
    for(int i=1;i<=n;i++)
    {
    
    
        scanf("%s",cell[i]+1);
    }
    for(int i=1;i<=rob;i++)
    {
    
    
        int p;
        scanf("%d",&p);
        add(S,p,1);
        add(p,S,0);
        add(p,p+m,0x3f3f3f3f);
        add(p+m,p,0);
    }
    for(int i=1;i<=exc;i++)
    {
    
    
        int p;
        scanf("%d",&p);
        int id=n*m*2+p;
        add(id,n*m*5+p,0x3f3f3f3f);
        add(n*m*5+p,id,0);
        add(n*m*5+p,T,0x3f3f3f3f);
        add(T,n*m*5+p,0);
    }
}
void build()
{
    
    
    for(int i=1;i<=n;i++)
    {
    
    
        for(int j=1;j<=m;j++)
        {
    
    
            if(cell[i][j]=='1')
                continue;
            int up=i*m+j,down=up+n*m,left=up+n*m*2,right=up+n*m*3;
            add(up,down,1);
            add(down,up,0);
            add(left,right,1);
            add(right,left,0);
            add(down,left,0x3f3f3f3f);
            add(left,down,0);
            add(right,up,0x3f3f3f3f);
            add(up,right,0);
            if(cell[i-1][j]!='1'&&i-1>=1)
            {
    
    
                add(down,(i-1)*m+j,0x3f3f3f3f);
                add((i-1)*m+j,down,0);
            }
            if(cell[i+1][j]!='1'&&i+1<=n)
            {
    
    
                add(down,(i+1)*m+j,0x3f3f3f3f);
                add((i+1)*m+j,down,0);
            }
            if(cell[i][j-1]!='1'&&j-1>=1)
            {
    
    
                add(right,left-1,0x3f3f3f3f);
                add(left-1,right,0);
            }
            if(cell[i][j+1]!='1'&&j+1<=m)
            {
    
    
                add(right,left+1,0x3f3f3f3f);
                add(left+1,right,0);
            }
        }
    }
}
bool bfs()
{
    
    
    memset(dis,0x3f,sizeof dis);
    queue<int> q;
    q.push(S);
    dis[S]=0;
    while(q.size())
    {
    
    
        int u=q.front();
        q.pop();
        for(int i=h[u];i!=-1;i=ne[i])
        {
    
    
            int v=e[i];
            if(dis[v]==0x3f3f3f3f&&capacity[i]-flow[i]>0)
            {
    
    
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    return dis[T]!=0x3f3f3f3f;
}
int dfs(int u,int limit)
{
    
    
    if(u==T)
        return limit;
    int delta=limit;
    for(int& i=curedge[u];i!=-1;i=ne[i])
    {
    
    
        int v=e[i];
        if(dis[v]==dis[u]+1&&capacity[i]-flow[i]>0)
        {
    
    
            int d=dfs(v,min(delta,capacity[i]-flow[i]));
            flow[i]+=d;
            flow[i^1]-=d;
            delta-=d;
        }
        if(delta==0)
            break;
    }
    return limit-delta;
}
int dinic()
{
    
    
    int ans=0;
    while(bfs())
    {
    
    
        for(int i=0;i<=6*n*m;i++)
        {
    
    
            curedge[i]=h[i];
        }
        ans+=dfs(S,0x3f3f3f3f);
    }
    return ans;
}
int main()
{
    
    
    int t;
    scanf("%d",&t);
    while(t--)
    {
    
    
        init();
        input();
        build();
        int ans=dinic();
        if(ans==rob)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45812711/article/details/113813494