zoukankan      html  css  js  c++  java
  • 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
    ";
    	else cout << "No
    ";
        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
    ",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
    ",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
    ",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)sumlimits_{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) sumlimits_{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
    ",f.c[1][i]);
        return 0;
    }
    
    
  • 相关阅读:
    支付宝接口对接常见问题
    Myeclipse中配置安卓环境
    算法精解概述
    使用Eclipse开始Java编程
    Windows使用SSH管理Ubuntu
    大臣的旅费
    剪格子
    波动数列
    买不到的数目
    逆波兰表达式
  • 原文地址:https://www.cnblogs.com/HotPants/p/14706700.html
Copyright © 2011-2022 走看看