八月十七日个人训练小结(补题)


前言

今天主要是补题,牛客,杭电和落下的cf

一、杭电补题

1、Shortest Path in GCD Graph

答案只有1和2,开始想用最短路,最后还是没能解决,这不是一个最短路问题,算是思维题,找与给定两个数都互质的数,就是对a,b分解质因数,符合题目要求的中转数就是不含这些因数的数,容斥处理

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>f;
const int N = 1e7+10,mod = 998244353;
int phi[N],primes[N];
bool st[N];
int n, m, len ;
ll ans;
int cnt;
void get_eulers(int n)
{
    st[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (!st[i])
        {
            primes[cnt++] = i;
            phi[i] = i; 
        }
        for (int j = 0; primes[j] * i <= n; j++)
        {
            st[primes[j] * i] = 1;
            phi[primes[j] * i] = primes[j];
            if (i % primes[j] == 0)
            {
                break;
            }
        }
    }
}

function<void(int, int, int) > dfs = [&](int u, int mul, int sign){//容斥暴力写法
    if (u == len){
        ans = (ans + 1LL * sign * (n / mul)) % mod;
        return;
    }
    dfs(u + 1, mul, sign);
    if (1LL * mul * f[u] <= n) dfs(u + 1, mul * f[u], mod - sign);
};

void slove(){
   scanf("%d%d",&n,&m);
   get_eulers(n);
   

   while(m--){
    int u,v;
    scanf("%d%d",&u,&v);
    // if(u > v) swap(u,v);
    int sx = __gcd(u,v);
    if(sx == 1){
        printf("1 1\n");
        continue ;
    }
    f.clear();
    while(u > 1) f.push_back(phi[u]),u /= phi[u];
    while(v > 1) f.push_back(phi[v]),v /= phi[v];
    sort(f.begin(),f.end());
    f.erase(unique(f.begin(),f.end()),f.end());
    len = f.size();

    ans = 0;
    if(sx == 2) ans = 1;
    dfs(0, 1, 1);
    printf("2 %d\n",ans);

   }
    
    

}

int main(){
    slove();
    return 0;
}

2、Matryoshka Doll

dp,参考大佬的思路代码

我们定义 dp[i][j] 为 [1...i] 中分成 j 组的方案数。

我们可以 O(n2) 处理出,对于每个 a[i] ,左边有多少个数是不能放在同一组的。我们定义为 L[i] 。

那么显然,剩下的 i−L[i] 的数,都可以和 a[i] 放到同一组。(注意题目是有序的

a[i] 不能和这 L[i] 数一组,并且这 L[i] 个数,也是不能放到同一组的

那么转移。我们考虑 dp[i][j] 可以如何转移?

(1) a[i] 直接一组,那么有 dp[i][j]+=dp[i−1][j−1]

(2) a[i] 和前面的一些数组成一组,前面 [1...i−1] 已经组成了 j 组了。前面 a[i] 可以选择的组数为 j−L[i] 。(L[i] 个数不能和 a[i] 同组,并且这 L[i] 个数都位于不同的组

dp[i][j]+=dp[i−1][j]∗(j−L[i])

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010,mod = 998244353;
int n, k, r;
int a[N],L[N];
int f[N][N];

void slove() {
    cin >> n >> k >> r;
    for (int i = 1; i <= n; i++)cin >> a[i];
    for (int i = 1; i <= n; i++) {
        L[i] = 0;
        for (int j = i - 1; j >= 1; j--)
            if (abs(a[i] - a[j]) < r) {
            L[i]++;
        }
    }
    for (int i = 0; i <= n + 2; i++)for (int j = 0; j <= n + 2; j++)f[i][j] = 0;
    f[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            f[i][j] = f[i - 1][j - 1] + ((ll)f[i - 1][j] * (j - L[i])) % mod;
            f[i][j] %= mod;
        }
    }
    cout << f[n][k] << endl;
}

int main(){
    
    int tt;
    cin >> tt;
    while(tt--){
        slove();

    }

    return 0;
}

二、牛客补题

1、Everyone is bot

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+10;
vector<int>v;
ll n, m;
ll a[N][N];
 
int slove()
{
    cin >> n >> m;
     
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
            cin >> a[i][j];
        
 
    for(int i = 1; i <= n % m; i ++) cout << a[i][i] << " ";
    for(int i = n % m + 1; i <= n; i ++)cout << 0 << " ";
 
    return 0;
}

int main(){
	slove();
	return 0;
}

2、Maimai DX 2077

题目有点难懂,理解后还是很简单的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>v;
const int N = 1;
int n, m ;
double ans1 = 0,ans2 =0,res1 = 0,res2 = 0;
void slove(){
	int a,b,c,d,e;
	
	cin >> a >> b >> c >> d >> e;
	ans1 += (a * 1.0 + b * 1.0 + c * 0.8 + d * 0.5);
	ans2 += (a +b + c + d + e) * 1.0;

	cin >> a >> b >> c >> d >> e;
	ans1 += (a* 2.0 + b * 2.0 + c * 1.6 + d * 1.0);
	ans2 += (a +b + c + d + e) * 2.0;

	cin >> a >> b >> c >> d >> e;
	ans1 += (a * 3.0 + b * 3.0 + c * 2.4 + d * 1.5);
	ans2 += (a +b + c + d + e) * 3.0;

	cin >> a >> b >> c >> d >> e;
	ans1 += (a* 5.0 + b * 5.0 + c * 2.5 + d * 2.0);
	ans2 += (a +b + c + d + e) * 5.0;

	res1 += (a * 1.0 + b * 0.5 + c * 0.4 + d * 0.3);
	res2 += (a +b + c + d + e) * 1.0;
	printf("%.10lf\n",ans1 / ans2 * 100 + res1 / res2);

	
	

}

int main(){
	slove();
	return 0;
}

3、Here is an Easy Problem of Zero-chan

以为是lca题目,写了好久,直接lca是必然t的,转换思路,如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 +10;
const ll mod = 1e13;

int n,m;

int h[N],e[N*2],ne[N*2],idx;
int depth[N],fa[N];//depth深度数组,fa[i][j]记录i走2^j步后的祖先
int q[N];//数组模拟队列
int tol;
ll sx,sy;
bool st1[N],st2[N];
int a[N],b[N],sa;
int cnt[2][N];
void init(){
    for(int i = 1; i < N; i ++){
        int j = i;
        int s = 0;
        while(j % 2 == 0){
            s ++;
            j /= 2;
        }
        cnt[0][i] = s;
        s = 0;
        while(j % 5 == 0){
            s ++;
            j /= 5;
        }
        cnt[1][i] = s;
    }
}

void add(int a,int b){
    e[++idx] = b, ne[idx] = h[a], h[a] = idx;
}

void dfs(int u,int f)//u根节点
{
    fa[u] = 1;
    for(int i = h[u]; i; i = ne[i]){
        int j = e[i];
        if(j == f) continue;
        dfs(j,u);
        fa[u] += fa[j];
    }
}


void dfs1(int u,int f){
    for(int i = h[u]; i; i = ne[i]){
        int j = e[i];
        if(j == f) continue;
        int te = fa[u] - fa[j];
        a[j] += a[u] + te * cnt[0][u];
        b[j] += b[u] + te * cnt[1][u];
        dfs1(j,u);
    }
}

int main(){
    scanf("%d %d",&n,&m);
    init();
    for(int i = 1; i < n; i ++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }
    
    dfs(1,0);
    dfs1(1,0);
    while(m--){
        int su;
        scanf("%d",&su);
        if(su == 1) puts("0");
        else{
            sx = a[su] + fa[su] * cnt[0][su];
            sy = b[su] + fa[su] * cnt[1][su];
            printf("%lld\n",min(sx,sy));
        }   
    }
    return 0;
}

4、想要更多的0

dp+二分

数位dp求[l,r]区间内的0的个数,但是因为单个0也算一个0,所以需要check判断

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
int n,m,t,k;

int f[20][20];
int a[50];

ll dp(int id,int d,int sum,int tag,int lead)
{
    if(!id) return sum;
    if(!tag&&lead&&~f[id][sum]) return f[id][sum];

    ll res=0,maxx=tag?a[id]:9;

    for(int i=0;i<=maxx;i++)
    {
        res+=dp(id-1,d,sum+((i||lead)&&(i==d)),tag&&a[id]==i,lead||i);
    }

    if(!tag&&lead) f[id][sum]=res;
    return res;
}

int calc(int x,int d)
{
    memset(f,-1,sizeof f);
    int cnt=0;
    while(x) a[++cnt]=x%10,x/=10;
    return dp(cnt,d,0,1,0);
}

int check(int x)
{
	if(x==0) return calc(n,0)>=k-1;  //x为0时,则只需大于等于k-1,减去单独的0
	else return calc(n,0)-calc(x-1,0)>=k;

}

void solve()
{
	cin>>n>>k;
	int l=0,r=n;
	while(l<r)
	{
		int mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}

	if(!check(l)) cout<<"-1"<<endl;
	else cout<<l<<endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	solve();


	return 0;
}

三、cf补题

1、A. Chip Game

签到题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>v;

int n, m ;
void slove()
{
	cin>>n>>m;
	if((n+m)%2 == 0) cout<<"Tonya"<<endl; 
	else cout<<"Burenka"<<endl;
}

int main(){
	
	int tt;
	cin >> tt;
	while(tt--){
		slove();

	}

	return 0;
}

2、B. Mathematical Circus

如果 k 是奇数,一定有合适的方案——将所有奇数作为 ai ,所有偶数作为 bi 。

如果 k 是偶数, k 就不能改变数字的奇偶性。

  • 若 k 是 4 的倍数,则 (ai+k)∗bi≡ai∗bi(mod4) 。这样问题无解。
    • 这是因为,每个奇数只能与 4 的倍数配对,而奇数有 n/2 个, 4 的倍数小于 n/4 个
  • 若 k≡2(mod4) ,合适的方案为:将所有 4 的倍数作为 bi ,与奇数配对,其他偶数作为 ai ,与奇数配对,凑出 n/2 对。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>v;
int n, m ;
void slove()
{
	int n, k; cin>>n>>k;
	if(k%2 == 1)
	{
	    cout<<"YES\n";
	    for(int i=1;i<=n;i+=2) cout<<i<<" "<<i+1<<endl; 
	    return;
	}
	else
	{
	    if(k%4 == 2)
	    {
	        cout<<"YES\n";
	        for(int i=2;i<=n;i+=2)
	        {
	            if(i%4 == 2) cout<<i<<" "<<i-1<<endl;
	            else cout<<i-1<<" "<<i<<endl;
	        }
	    } 
	    else cout<<"NO\n";
	}
}

int main(){
	
	int tt;
	cin >> tt;
	while(tt--){
		slove();

	}

	return 0;
}

三、C. Fighting Tournament

待。。。。。。

明天继续


总结

数位dp,明天学习。这些思维题,需要再多看看,去想想为什么别人能去这么想呢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值