AtCoder Beginner Contest 199 題解


本場鏈接:AtCoder Beginner Contest 199

A - Square Inequality

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)


int main()
{
	Angel_Dust;
	int a,b,c;cin >> a >> b >> c;
	if(a * a + b * b < c * c)	cout << "Yes\n";
	else cout << "No\n";
    return 0;
}

B - Intersection

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 105;
int a[N],b[N];

int main()
{
	int n;scanf("%d",&n);
	forn(i,1,n)	scanf("%d",&a[i]);
	forn(i,1,n)	scanf("%d",&b[i]);

	int res = 0;
	forn(x,1,1000)
	{
		bool ok = 1;
		forn(i,1,n)
		{
			if(a[i] <= x && x <= b[i])	continue;
			ok = 0;
			break;
		}
		if(ok)	++res;
	}

	printf("%d\n",res);
    return 0;
}

C - IPFL

為了實現操作2,不妨將原來的整個字符串切割成S1,S2。對於操作2,直接swap(s1,s2)即可。對於操作1,討論位置在哪個字符串內再進行swap即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)


int main()
{
	Angel_Dust;
	int n;cin >> n;
	string s1,s2;cin >> s1;
	s2 = s1.substr(n,n);
	s1.resize(n);

	int q;cin >> q;
	while(q--)
	{
		int t,a,b;cin >> t >> a >> b;
		--a;--b;
		if(t == 1)
		{
			if(a < n && b < n)	swap(s1[a],s1[b]);
			if(a < n && b >= n)	swap(s1[a],s2[b - n]);
			if(a >= n && b < n)	swap(s2[a - n],s1[b]);
			if(a >= n && b >= n)	swap(s2[a - n],s2[b - n]);
		}
		else	swap(s1,s2);
	}

	cout << s1 << s2 << endl;
    return 0;
}

D - RGB Coloring 2

最粗暴的想法:直接枚舉每個點的顏色,再通過圖判斷方案是否合法,復雜度\(O(3^n)\)顯然是不可以接受的。

如果這個圖本身是聯通的,那么如果某個點的顏色被確定了之后,與他相鄰的點事實上只有\(2\)個選擇,進而可以推出實際上方案數是\(O(3*2^{n-1})\)的,復雜度就可以承受了。枚舉每個點的顏色判斷是否合法即可。

那么如果圖不連通怎么辦?對於每個獨立塊求完答案后乘法原理合並即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 22,M = N * N;
vector<int> E[N];
int col[N],n,m;
ll res;


void dfs(int u,ll ans)
{
	if(u == n + 1)	res += ans;
	else if(E[u].empty())	dfs(u + 1,ans * 3);
	else
	{
		forn(_,1,3)
		{
			bool ok = 1;
			for(auto& v : E[u])
				if(col[v] == _)
					ok = 0;
			if(ok)
			{
				col[u] = _;
				dfs(u + 1,ans);
				col[u] = 0;
			}
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	forn(_,1,m)
	{
		int u,v;scanf("%d%d",&u,&v);
		E[u].push_back(v);E[v].push_back(u);
	}

	dfs(1,1);

	printf("%lld\n",res);
    return 0;
}

E - Permutation

注意到如果我們把排列的構造換成某種二元關系(可以用0/1表達)的話,這個題可以狀壓表達當前的局面。

  • 狀態:\(f[S]\)表示當前選擇的數的集合是S的前提下,滿足所有\(x_i \leq |S|\)的約束條件的方案數。
  • 入口:\(f[0] = 1\)顯然
  • 轉移:考慮枚舉新加入的數\(x\),那么首先\(x\)不屬於S集合,其次在加入\(x\)之后會引入所有\(x_i = |s|+1\)的約數條件,直接判斷這些新加入的約束條件是否仍然滿足即可。
  • 出口:\(f[(1 << n) - 1]\)所有元素都選擇上的情況。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 18;
struct Node
{
    int x,y,z;
};
ll f[1 << N];
vector<Node> E[N];

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    f[0] = 1;
    forn(i,1,m)
    {
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        E[x].push_back({x,y,z});
    }

    forn(S,0,(1 << n) - 1)
    {
        forn(x,0,n - 1)
        {
            if(S >> x & 1)  continue;
            int sz = 1,ok = 1;
            forn(j,0,n - 1) if(S >> j & 1)  ++sz;
            for(auto& lim : E[sz])
            {
                int cnt = 0;
                if(x + 1 <= lim.y)  ++cnt;
                forn(j,0,n - 1) if((S >> j & 1) && j + 1 <= lim.y)  ++cnt;
                if(cnt > lim.z)
                {
                    ok = 0;
                    break;
                }
            }
            if(!ok) continue;
            f[S | (1 << x)] += f[S];
        }
    }

    printf("%lld\n",f[(1 << n) - 1]);
    return 0;
}

F - Graph Smoothing

形式非常套路的一道題,看到數據范圍就可以猜到矩陣快速冪了。

首先考慮一個暴力的DP

  • 狀態:\(f[i][k]\)表示\(i\)點權期望在\(k\)次操作之后的取值。
  • 入口:\(f[i][0] = a[i]\)
  • 轉移:每次隨機選擇一條邊,那么只有兩種情況:要么以\(1/m\)的概率選擇上一條邊與\(i\)相連,另一點記為\(v\),那么之后兩者會取平均。要么就\((m - deg_i) / m\)的概率選擇一條不與\(i\)相連的邊,此后權值不變。記\(S_i\)為所有與\(i\)點有一條邊直接相連的點集,那么轉移方程\(f[i][k] = (1/m)\sum\limits_{v \in S_i}(f[i][k - 1] + f[v][k - 1]) / 2 + ((m - deg_i)/m)f[i][k - 1]\)合並一下項可以得到:\(f[i][k] = 1 / (2*m) \sum\limits_{v \in S_i}f[v][k - 1] + (2 * m - deg_i) / (2 * m) f[i][k - 1]\)
  • 出口:\(f[i][k]\)

考慮矩陣快速冪把\(k\)優化掉,保留\(f[i]\)表示\(i\)點的期望權值。

考慮構造系數矩陣:如果\((u,v)\)之間有一條邊直接相連,在一次轉移的時候\(f[u]\)會向\(f[v]\)產生\(f[u] / (2 * m)\)的貢獻,則讓系數矩陣\(B[v][u] = 1 / (2 * m)\),反之亦然。對於每個點自身的貢獻:對應的直接讓\(B[i][i] = (2 * m - deg_i) / (2 * m)\)

最后答案就是\(f * B^k\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
typedef pair<int,int> pii;
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second

const int N = 105,MOD = 1e9+7;
pii edges[N * N];
int deg[N];

struct Mat
{
    int c[N][N];
    Mat()
    {
        memset(c,0,sizeof c);
    }
    Mat operator*(const Mat& r) const
    {
        Mat res;
        forn(i,1,N - 1) forn(j,1,N - 1) forn(k,1,N - 1) res.c[i][j] = (res.c[i][j] + 1ll*c[i][k] * r.c[k][j] % MOD) % MOD;
        return res;
    }
    void operator*(int r) 
    {
        forn(i,1,N - 1) forn(j,1,N - 1) c[i][j] = 1ll*c[i][j] * r % MOD;
    }
};

Mat qpow(Mat a,int b,int MOD)
{
    Mat res;forn(i,1,N - 1) res.c[i][i] = 1;
    while(b)
    {
        if(b & 1)   res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

int qpow(int a,int b,int MOD)
{
    int res = 1;
    while(b)
    {
        if(b & 1)   res = 1ll * res * a % MOD;
        a = 1ll * a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main()
{
    int n,m,k;scanf("%d%d%d",&n,&m,&k);
    Mat f,B;forn(i,1,n)   scanf("%d",&f.c[1][i]);

    forn(i,1,m)
    {
        int u,v;scanf("%d%d",&u,&v);
        ++deg[u];++deg[v];
        edges[i] = {u,v};
    }

    int m_2fact = qpow(m * 2,MOD - 2,MOD);
    
    forn(i,1,m)
    {
        int u = edges[i].x,v = edges[i].y;
        B.c[u][v] = m_2fact;
        B.c[v][u] = m_2fact;
    }

    forn(i,1,n) B.c[i][i] = (2 * m - deg[i]) * 1ll * m_2fact % MOD;
    

    B = qpow(B,k,MOD);
    f = f * B;
    forn(i,1,n) printf("%d\n",f.c[1][i]);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM