8.6 2020 Multi-University Training Contest 6题解及补题

目录
  • 8.6 2020 Multi-University Training Contest 6题解及补题
    • 比赛过程
    • 题解
      • 1001 Road To The 3rd Building
        • 题意
        • 解法
        • 代码
      • 1002 Little Rabbit's Equation
        • 题意
        • 解法
        • 代码
      • 1005 Fragrant numbers
        • 题意
        • 解法
        • 代码
      • 1006 A Very Easy Graph Problem
        • 题意
        • 解法
        • 代码
      • 1009 Divisibility
        • 题意
        • 解法
        • 代码
      • 1010 Expectation
        • 题意
        • 解法
        • 代码

8.6 2020 Multi-University Training Contest 6题解及补题

比赛过程

这场算是比较顺利的场,过了四道题,还是需要更加深入的配合。

题解

1001 Road To The 3rd Building

题意

给你n个数,现在任意选择一个区间求其平均值,你需要求出这个平均值的期望

解法

找规律,发现就是逆元的运算。

用到上次题解的逆元求1/i的逆元和

代码

#include 
using namespace std;
typedef long long ll;
ll gcd(ll T, ll b) { return b == 0 ? T : gcd(b, T % b); }
ll lcm(ll T, ll b) { return T / gcd(T, b) * b; }
#define IO ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
// int a[200020];
#define REP(i, a, n) for (int i = a; i < (n); ++i)
#define PER(i, a, n) for (int i = (n)-1; i >= a; --i)
// ll mod= 1000000007;
const int maxn=200020;
ll sum[maxn];
ll mod = 1e9 + 7;
ll inv[maxn];
void getInv(int n){
    inv[1] = 1;
    for (int i = 2; i <= n; i++) inv[i] = inv[mod % i] * (mod - mod / i) % mod;
}
ll cnt[maxn];
void init() {
    getInv(maxn);
    sum[1] = 1;
    REP(i, 2, maxn) { sum[i] = (sum[i - 1] + inv[i]) % mod; }
    return ;
}
void test(ll n) {
    cnt[1]=sum[n];
    for(ll i=2;i<=(n+1)/2;i++) {
        cnt[i]=cnt[i-1];
        cnt[i]=(cnt[i]+sum[n+1-i]-sum[i-1]+mod)%mod;
    }
}



int main() {
    IO;
    int T;
    cin>>T;
    init();
    while(T--) {
        int a[200020];
        int n;
        ll ans=0;
        cin>>n;
        test(n);
        for(int i=1;i<=n;i++) {
            cin>>a[i];
        }
        // cout<

1002 Little Rabbit's Equation

题意

判断给出的表达式在哪种进制下成立。

解法

直接把字符串截成三段,从小到大枚举每种进制即可。

代码

#include 
using namespace std;
typedef long long ll;
ll op(string s,int r){
    int len=s.length();
    ll ans=0;
    ll tmp=1;
    for(int i=len-1;i>=0;i--){
        int x;
        if(s[i]>='0'&&s[i]<='9'){
            x=s[i]-'0';
        }
        else{
            x=s[i]-'A'+10;
        }
        if(x>=r){
            return -1;
        }
        ans+=tmp*x;
        tmp*=r;
    }
    return ans;
}
ll ff(ll a,ll b,int p){
    if(p==1)return a+b;
    if(p==2)return a-b;
    if(p==3)return a*b;
    if(p==4){
        if(a%b==0)return a/b;
        else return -1;
    }
}
int main() {
    string s;
    while(cin>>s){
        string a,b,c;
        ll aa,bb,cc;
        int p=0;
        int p1,p2;
        int len=s.length();
        for(int i=0;i

1005 Fragrant numbers

题意

给一个无限循环的字符串1145141919,可以取一个前缀并在中间添加+,*(,)使其变成一个运算表达式,
每次询问一个n,输出构成表达式结果等于n的最小前缀。如果不存在输出-1

解法

经过打表处理可以发现,只需要前13个以内就可以构造出所有合法状态,然后就是区间dp了。要
枚举区间长度,左端点和右端点,枚举$a[l][mid],a[mid+1][r]的状态。

代码

#include 
using namespace std;
typedef long long ll;
const int mac=50;
int a[mac],ans[5050];
vectordp[50][50];
bool vis[50][50][5010];
ll get_num(int l,int r)
{
    ll ans=0;
    for (int i=l; i<=r; i++)
        ans=ans*10+a[i];
    return ans; 
}
int main(int argc, char const *argv[])
{
    string s="1145141919";
    s+=s;
    int len=s.length();
    for (int i=0; i5000) continue;
            dp[i][j].push_back(p);
            vis[i][j][p]=1;
        }
    }
    for (int i=2; i<=15; i++){
        for (int l=1; l+i-1<=15; l++){
            int r=l+i-1;
            for (int mid=l; mid<=r; mid++){
                for (auto x:dp[l][mid]){
                    for (auto y:dp[mid+1][r]){
                        if (x+y<=5000 && !vis[l][r][x+y]){
                            dp[l][r].push_back(x+y);
                            vis[l][r][x+y]=1;
                        }
                        if (x*y<=5000 && !vis[l][r][x*y]){
                            dp[l][r].push_back(x*y);
                            vis[l][r][x*y]=1;
                        }
                    }
                }
            }
        }
    }
    for (int i=1; i<=15; i++){
        for (auto x:dp[1][i]){
            if (x<=5000 && !ans[x]) ans[x]=i;
        }
    }
    int t;
    scanf ("%d",&t);
    while (t--){
        int n;
        scanf ("%d",&n);
        if (ans[n]) printf("%d\n",ans[n]);
        else printf("-1\n");
    }
    return 0;
}

1006 A Very Easy Graph Problem

题意

在一个n个结点,m条边的无向连通图中,且第i条边的权值为2i,每个结点有一个值,为1或者0。d(i,j)表示结点i到结点j之间的最短距离。对所有节点求所有的可能配对形式d(i,j)*[a[i]1^a[0]0]的和,最后对1e9+7求余。

解法

注意这个权值是比较特殊的,也就是如果第i条边(权值为2i)是连接结点j和结点k,但是在这之前j和k已经相连了,那么这条边(权值2i)永远都不会被最短路经过,因为前i-1条边的总权值是2i-2,小于2i.所以我们便可以将这个无向连通图转换为一棵最小生成树(既是最小生成树也是任意两个结点之间的距离都是最小)。这里的n,m都是1e5的数量级别,所以如果暴力求两个结点的最短路那么肯定会超时。所以我们可以转换一下思路,求每条边的贡献值,也就是如果这条边最后会出现在最短路中,我们需要计算的是在最短路中出现了多少次,可以求出这条边两边各自的1和0的数量,那么这条边在最短路中的次数就是left_0right_1+left_1right_0,那么这条边的在最后的结果中的贡献就是(left_0right_1+left_1right_0)*这条边的权值。那么现在主要的就是求出每条边两边的1和0的数量,可以使用dfs进行求解。

代码

#include 
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5 + 5;
const int inf = ~0u >> 1;
typedef pair P;
#define REP(i, a, n) for (int i = a; i < (n); ++i)
#define PER(i, a, n) for (int i = (n) - 1; i >= a; --i)
const ll mod = 1e9 + 7;
vector > e[maxn];
int a[maxn];
int fa[maxn];
ll ans = 0, zero, one;
int init() {
    ans = zero = one = 0;
    REP(i, 0, maxn) {
        fa[i] = i;
        e[i].clear();
    }
}
int find(int x) //用于查找一个元素的所在集合即根节点
{
    if (fa[x] != x) fa[x] = find(fa[x]); //路径压缩,递归将路径上的元素父亲指针都指向根节点
    return fa[x]; 
}
void merge(int r1, int r2) //合并两个集合,r2合并到r1
{
    r1 = find(r1);
    r2 = find(r2);
    if (r1 != r2) fa[r2] = r1;
}

pair dfs(int rt,int fa,long long edge){
    pair tmp(0,0);
    for(int i = 0; i < e[rt].size(); ++i){
        int to = e[rt][i].first;
        if(to == fa) continue;
        pair tmp2 = dfs(to,rt,e[rt][i].second);
        tmp.first += tmp2.first;
        tmp.second += tmp2.second;
    }
    if(a[rt] == 0) tmp.first ++;
    else tmp.second ++;
    ans += ((1ll * (zero - tmp.first) * tmp.second + 1ll * (one - tmp.second) * tmp.first) % mod * edge % mod);
    return tmp;
}
int main() {
    IO;
    int t;
    cin >> t;
    while (t--) {
        init();
        int n, m;
        cin >> n >> m;
        REP(i, 1, n + 1) {
            cin >> a[i];
            if (a[i]) {
                one++;
            }
            else {
                zero++;
            }
        }
        ll cal = 1;
        REP(i, 1, m + 1) {
            cal = cal * 2 % mod;
            int u, v;
            cin >> u >> v;
            if (find(u) != find(v)) {
                e[u].push_back(pair(v, cal));
                e[v].push_back(pair(u, cal));
                merge(u, v);
            }
        }
        dfs(1, -1, 0);
        cout << ans % mod << endl;
    }
    return 0;
}

1009 Divisibility

题意

给你一个10进制的b和x,对于任意的一个b进制的y。如果y每一位的和可以被x整除,且y可以被x整除;或者如果y每一位的和不可以被x整除,且y不可以被x整除。那么就输出T。否则输出F

解法

思维题,发现b--之后,b取余x等于0就T的。

代码

#include 
using namespace std;
typedef long long ll;
int main() {
    int t;
    scanf("%d",&t);
    while(t--){
        ll b,x;
        scanf("%lld%lld",&b,&x);
        b--;
        if(b%x==0){
            cout<<"T"<

1010 Expectation

题意

给一个图,输出其随机生成树的权值的期望,其权值为树的所有边的按位与。

解法

用到了矩阵树定理和期望的知识。矩阵树定理可以求出一个图内生成树的数目,也就是求期望要用到的总数。
分子则是所有边的按位与。因为E(x+y)=E(x)+E(y),所以可以按照二进制的位数分别计算31位每一位的贡献,
遍历每一位,每次都用矩阵树定理算一次答案即可。

代码

#include
using namespace std;
#define ll long long
const ll mod=998244353;
ll n,m,t,sum;
ll f[207][207];
ll u[40007],v[40007],w[40007];
ll gauss () {
    ll ans = 1 ;
    for (int i = 1; i < n; i ++) {
        for (int j = i + 1; j < n; j ++) {
            while (f[j][i]) {
                ll t = f[i][i] / f[j][i] ;
                for (int k = i; k < n; k ++)
                    f[i][k] = (f[i][k] - t * f[j][k] + mod) % mod ;
                swap (f[i], f[j]) ;
                ans = -ans ;
            }
        }
        ans = (ans * f[i][i]) % mod ;
    }
    return (ans + mod) % mod ;
}
long long int pow(long long int x,long long int n,long long int mod)
{
    long long int res=1;
	while(n>0)
	{
	   if(n%2==1)
	   {
	   	 res=res*x;
	   	 res=res%mod;
	   }
	   x=x*x;
	   x=x%mod;
	   n>>=1;
	}
	return res;
}
void add(int u,int v){
	f[u][u]++;f[v][v]++;
	f[u][v]--;f[v][u]--;
}
void init(){
	memset(f,0,sizeof(f));
}
int main(){
	scanf("%lld",&t);
	while(t--){
		init();
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=m;i++){
			cin>>u[i]>>v[i]>>w[i];
			add(u[i],v[i]);
		}
		sum=gauss();
		sum=pow(sum,mod-2,mod);
		ll aqours=0,er=1;
		for(int i=0;i<=30;i++){
			init();
			for(int i=1;i<=m;i++){
				if(w[i]&er){
					add(u[i],v[i]);
				}
			}
			aqours=(aqours+gauss()*er%mod*sum%mod)%mod;
			er*=2;
		}
		printf("%lld\n",aqours);
	}
}

你可能感兴趣的:(8.6 2020 Multi-University Training Contest 6题解及补题)