Atcoder beginner contest 199

Atcoder ABC 199

A,B跳过

C:

题意:

给定一个字符串,有两种操作,第一种操作将两个字符调换,第二种操作将左右交换,输出最后的字符串。

解法:

通过题可以看出来我们需要用 \(O(n+q)\) 的时间复杂度的算法,第二种操作肯定不能直接用,因为 \(string\) 赋值也需要 \(O(n)\) 的时间复杂度。

因此,我们设置一个cnt用来记录左右交换的次数:

  1. \(n|2\), 为 \(l,r\);
  2. \(!n|2\),为 \(r,l\);

我们据此可以写出来交换的式子,分三种情况讨论即可。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,q,cnt;
string s,l,r,r2,l2;
int main()
{
    cin>>n; cin>>s;
    for(int i=0;i<n;i++) l+=s[i];
    for(int i=n;i<2*n;i++) r+=s[i];
    cin>>q;
    while(q--){
        int t,a,b;
        scanf("%d%d%d",&t,&a,&b);a--,b--;
        if(t==1){
            char ch;
            if(cnt%2==0){
                if(a>=n&&b>=n) a=a-n,b=b-n,ch=r[a],r[a]=r[b],r[b]=ch;
                else if(a<n&&b>=n)   b=b-n,ch=l[a],l[a]=r[b],r[b]=ch;
                else                       ch=l[a],l[a]=l[b],l[b]=ch;
                
            }
            else{
                if(a>=n&&b>=n) a=a-n,b=b-n,ch=l[a],l[a]=l[b],l[b]=ch;
                else if(a<n&&b>=n)   b=b-n,ch=r[a],r[a]=l[b],l[b]=ch;
                else                       ch=r[a],r[a]=r[b],r[b]=ch;
            }
        } 
        else cnt++;
    }
    if(cnt%2==0) cout<<l<<r<<endl;
    else cout<<r<<l<<endl;
    system("pause");
    return 0;
}

D:

题意:

给你三种颜色和一个简单图,两个相邻的点(有路径)的颜色不同,问有多少种涂色方案。

解法:

点分两种:

  1. 独立点
  2. 有边点

对于独立点,我们选哪一种颜色就行,对于有边点,我们要进行深搜

可以这么想:我们枚举节点1的颜色,那么其连着的点就剩了两种颜色可以选,枚举颜色进行深搜即可。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int cnt[N],col[N],n,m;
#define ll long long
ll ans=0;
vector<ll> v[25]; 
ll dfs(ll x){
    if(x>n) return 1;
    if(v[x].size()==0) return 3*dfs(x+1);//空点,填什么颜色都可以
    int res=0,color,i;
    for(color=1;color<=3;color++){//选择填的颜色
        for(i=0;i<v[x].size();i++)
            if(col[v[x][i]]==color) break;
        if(i!=v[x].size()) continue;//是否颜色相同 
        else{
            col[x]=color;
            res+=dfs(x+1);
            col[x]=0;
        }
    }
    return res;
}
int main()
{
    cin>>n>>m;
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    } 
    for(int i=1;i<=3;i++){
        col[1]=i;//对于第一个点的颜色,我们可以有3种颜色可以选
        ans+=dfs(2);//从第二个点开始深搜
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}

E:

题意:

给定一个长为 \(n=\{1,2,3....n\}\) 的不可重序列,给定 \(m\) 个约束条件,即在第 \(1-x\) 的位上,最多有 \(z\) 个数小于等于 \(y\),求这种序列的个数。

做法:

看到数据范围一眼状压,正好选择数字可以看成一个01串。

对于dp,我们有这么一个状态转移方程:

\(dp[i|x]+=dp[i]\)

\(i|x\) 属于一个交集。

for(int j=0,x=1;j<n;j++,x<<=1)//枚举 另一集合的情况
    if(!b[j]){//第几位没用,也就是不重复
        bool flag=true;
        for(int k=0;k<v[b.count()+1].size();k++){
            int y=v[b.count()+1][k].first,z=v[b.count()+1][k].second;
            int c=(j+1<=y)+f[y];//第y位原来用过的数字+(是否现在的数+1>y)=现在总数字
            if(c>z){//数量多了
                flag=false;break;
            }
        }
        if(flag) dp[i|x]+=dp[i];//集合的并集+情况
    }

这里面的 \(x\) 表示新的一位,这样计算就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define mk make_pair
#define pii pair<long long ,long long>
#define ll long long
#define int long long
int n,m;
const int N=20;
int dp[1<<N];
vector<pii> v[N];
signed main()
{
    cin>>n>>m;
    for(int i=1,x,y,z;i<=m;i++){
        scanf("%lld%lld%lld",&x,&y,&z);
        v[x].push_back(mk(y,z));
    }
    dp[0]=1;
    for(int i=0;i<(1<<n);i++){//枚举所有用没用的情况
        bitset<20> b(i);//用了哪些数字
        ll f[20]={0};
        for(int j=0;j<n;j++) f[j+1]=f[j]+b[j];//在第k+1位上用了f[j]+b[j]个数
        for(int j=0,x=1;j<n;j++,x<<=1)//枚举 另一集合的情况
            if(!b[j]){//第几位没用,也就是不重复
                bool flag=true;
                for(int k=0;k<v[b.count()+1].size();k++){
                    int y=v[b.count()+1][k].first,z=v[b.count()+1][k].second;
                    int c=(j+1<=y)+f[y];//第y位原来用过的数字+(是否现在的数+1>y)=现在总数字
                    if(c>z){//数量多了
                        flag=false;break;
                    }
                }
                if(flag) dp[i|x]+=dp[i];//集合的并集+情况
            }
    }
    cout<<dp[(1<<n)-1]<<endl;
    system("pause");
    return 0;
}

F:

题意:

给一个 \(n\) 个顶点 \(m\) 条边的图,每一个顶点有一个权值,我们每次选择一条边,将边的两个顶点的权值编程两个顶点权值的平均值,求经过 \(q\) 次操作每个点权值的期望。

解法:

看成矩阵去计算....(不理解QWQ)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,mod=1e9+7;
int val[N],deg[N];
#define ll long long
#define vll vector<ll>
#define vlll vector<vll>
vlll operator*(const vlll &x,const vlll &y){//矩阵乘法
    vlll res(x.size(),vll(y[0].size()));
    for(int i=0;i<x.size();i++)
        for(int j=0;j<y[0].size();j++)
            for(int k=0;k<y.size();k++)
                res[i][j]=(res[i][j]+1ll*x[i][k]%mod*y[k][j]%mod)%mod;
    return res;
}
vlll dp(vlll a,ll b){//矩阵快速幂
    vlll res(a.size(),vll(a[0].size()));
    for(int i=0;i<a.size();i++) res[i][i]=1;
    while(b){
        if(b&1) res=res*a;a=a*a;b>>=1;
    }
    return res;
}
ll qmi(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;a=a*a%mod;b>>=1;
    }
    return res;
}
int main()
{
    int n,m,k;
    cin>>n>>m>>k;
    vlll a(n,vll(n));
    vlll b(1,vll(n));
    for(int i=0;i<n;i++) cin>>b[0][i];
    for(int i=0,x,y;i<m;i++){
        cin>>x>>y;x--,y--;
        deg[x]++;deg[y]++;
        a[x][y]=qmi(2*m,mod-2);
        a[y][x]=qmi(2*m,mod-2);
    }
    for(int i=0;i<n;i++) a[i][i]=1ll*(2*m-deg[i])*qmi(2*m,mod-2)%mod;
    vlll ans=b*dp(a,k);
    for(int i=0;i<n;i++) cout<<ans[0][i]<<endl;
    system("pause");
    return 0;
}

结束!

posted @ 2021-04-25 11:09  Evitagen  阅读(250)  评论(3编辑  收藏  举报