2023牛客寒假算法基础集训营3_20230120「数学礼包」「构造+哥巴猜想」「期望dp正反算法」

7/11

这次的题比上次顺手得多,虽然我思维也没有很好,但是跟其他方面比,我可能还是更喜欢思维+数学>算法>数据结构。

不需要什么知识的小思维对jls来说就是签(%%%

已过非太水的题们

//B找规律||小思维
//https://ac.nowcoder.com/acm/contest/46811/B

//画图即得递推式。

#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;

void work() {
    int n;cin>>n;
    int ans=0;
    if(n==1)ans=1;
    else if(n==2) ans=-1;
    else if(n==3) ans=2;
    else if(n==4) ans=2;
    else if(n==5) ans=3;
    else if(n%2==0){
        ans=n/2+n/6;
    }else{
        n--;ans=n/2+n/6+1;
    }
    cout<> t;
	while (t--) {
		work();
	}
	return 0;
}
//C小构造
//https://ac.nowcoder.com/acm/contest/46811/C

//除了1237以外任何数都可以由456相加得到,提前构造456拼拼凑凑即可。

#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;

void work() {
    int n;cin>>n;
    if(n==1||n==2||n==3||n==7){
        cout<<"-1\n";return;
    }
    int t=n%4;
    if(t==0){
        for(int i=1;i
//D小博弈
//https://ac.nowcoder.com/acm/contest/46811/D

//偶数总可以变成奇数,而奇数只可以变成偶。
//1(奇数)为必败态。奇数后手胜,偶数先手胜。

#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;

void work() {
    int n;cin>>n;
    if(n%2==1) cout<<"yukari\n";
    else cout<<"kou\n";
}

signed main() {
	io;
	work();
	return 0;
}
//E
//https://ac.nowcoder.com/acm/contest/46811/E

//搞搞全等三角形即可。

#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;

void work() {
    double a,b,c,d;
    cin>>a>>b>>c>>d;
    double dx=(a-c)/2;
    double dy=(b-d)/2;
    double x=(a+c)/2-dy;
    double y=(b+d)/2+dx;
    if(abs(x-(int)x)<1e-5&&abs(y-(int)y)<1e-5) 
        cout<<(int)x<<" "<<(int)y<<'\n';
    else
        cout<<"No Answer!\n";
}

signed main() {
	io;
	work();
	return 0;
}
//G
//https://ac.nowcoder.com/acm/contest/46811/G

//我写的像答辩一样的小小dfs

#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int x=0,num=1,stop;
string s;

int qp(int A,int B,int K){
    int ans = 1;
	A = A % K;
	while (B) {
		if (B % 2 == 1)
			ans = (ans * A) % K;
		B /= 2;
		A = (A * A) % K;
	}
	return ans;
}

void dfs(int cur,int ans,string st,char si){
    int res=0;
    
    for(int i=cur;;++i){
        
        if(s[i]=='='){
            //cout<='0'&&s[i]<='9'){
            res*=10;res+=s[i]-'0';
            st+=s[i];
        }
        
        else if(s[i]=='?'){
            if(si=='+'){
                ans+=res;res=0;
            }else if(si=='-'){
                ans-=res;res=0;
            }else if(si=='#'){
                ans=qp(ans,ans,res);
                res=0;
            }
            dfs(i+1,ans,st+'+','+');
            dfs(i+1,ans,st+'-','-');
            if(ans>0){
                dfs(i+1,ans,st+'#','#');
            }
            break;
        }
        
        else{
            if(si=='+'){
                ans+=res;res=0;
            }else if(si=='-'){
                ans-=res;res=0;
            }else if(si=='#'){
                ans=qp(ans,ans,res);
                res=0;
            }
            si=s[i];
            st+=s[i];
        }
    }
}

void work() {
    cin>>s;
    int len=s.length();
    for(int i=len-1;i>=0;--i){
        if(s[i]=='='){
            stop=i;
            break;
        }
        x+=(s[i]-'0')*num;
        num*=10;
    }
    dfs(0,0,"",'+');
    cout<<"-1\n";
}

signed main() {
	io;
	work();
	return 0;
}

K.

 K-永恒守候的爱恋_2023牛客寒假算法基础集训营3 (nowcoder.com)

思路:

 数学大礼包!

因子个数=分解质因数的所有指数+1的乘积。所以我们可以知道只有不同的因子个数对答案有贡献,该因子是什么并无影响。

最优构造应该是令不同的因子轮很多遍,每轮只出现一次,可以把出现次数看作一个塔,每一轮就是一层,输入也很友善就是有n个酱紫的层,我们还是可以用差分来计数。

对于每一层中的ans贡献都是比同层之前的加了一个出现过的因子,即在第一层里每加一个数,就有一个“1”变成了“2”,即答案增加之前的二倍,依此类推。

对于不同层间,发生改变不同了,例如从第一层是从1->2,第二层是从2->3,相当于不同层的等比数列求和结果相加,在层与层衔接处我们可以记录下每层的结尾,乘上层的公比即是上层首项,本层首项乘公比的项数-1次方即末项。

  • 一些合理性解说:因为我们要最优,那一定是把越多的贡献放到越前使加的次数更多,所以如果当前数在本层出现,那说明当前数一定在之前层都出现过,如果没有出现,那在更上层也不可能出现,即这个塔是金字塔(?,下层至少大于等于上层,所以每一层中变化一定相同,可以证明是严格等比数列。

注意这一切操作都在模意义下进行。

#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 1000000007
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int c[N];

ll qp(ll a,ll b,ll m){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%m;
        a=a*a%m;
        b>>=1;
    }
    return ans;
}

int inv(int a,int b){
    return qp(a,b-2,b);
}

void work() {
    int n;cin>>n;
    mem(c,0);
    for(int i=1;i<=n;++i){
        int a;cin>>a;
        c[1]++;c[a+1]--;        //差分
    }
    for(int i=1;i<=200005;++i){
        c[i]+=c[i-1];
    }
    int a1=1,last=1;
    int q;
    int ans=0;
    for(int i=1;;++i){
        if(c[i]==0) break;
        q=(i+1ll)*inv(i,mod)%mod;//公比
        a1=last*q%mod;           //首项
        int res=a1%mod*(qp(q,c[i],mod)-1+mod)%mod*inv((q-1+mod)%mod,mod)%mod;
            //其实等比数列求和的分母不需要逆元,他是一定可以整除的,但是写了也无妨
        (ans+=res)%=mod;
        last=a1*qp(q,c[i]-1,mod)%mod;//末项
    }
    ans%=mod;
    cout<

 I.

I-灵魂碎片的收集_2023牛客寒假算法基础集训营3 (nowcoder.com)

思路:

好数学的思维,妙妙。

为了便于求和,我们最好令n由少量的素数相乘得到。

从题目中给的特判入手,发现当x为偶数的时候我们可以酱紫构造:如果x-1是素数,令n=(x-1)*(x-1),S(n)=1+x-1=x;如果x-3是素数,令n=2*(x-3),S(n)=1+2+x-3=x。

当x为奇数时,如果n是1个素数那么答案为1,不行。如果n是两个素数乘积,那么我们就要找p,q,令1+p+q=x(x为奇),<=>p+q==x-1,哥德巴赫猜想已经证明在很大很大的范围内对于每一个偶数,我们总可以找到一对奇素数之和等于该偶数,所以只需要枚举就可以辣!

小于7的数要特判一下。

#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 1e6 + 50;
int ans[]= {0,-1,-1,4,9,-1,25,8};
bool is[N];
int tot;
int prime[N];

void euler_sieve() {
	tot = 0;
    is[1]=1;
	for (int i = 2; i < N; ++i) {
		if (!is[i]) prime[tot++] = i;//0为素数
		for (int j = 0; j < tot && prime[j]*i < N; ++j) {
			is[prime[j]*i] = 1;
			if (i % prime[j] == 0)
				break;
		}
	}
}

void work() {
    int x;
    cin>>x;
    if(x<=7){
        cout<> t;
	while (t--) {
		work();
	}
	return 0;
}

 H.

H-穿越万年的轮回_2023牛客寒假算法基础集训营3 (nowcoder.com)

谔谔,怎么写这题之前还要学学期望dp鸭我有点急。

学了一下,感觉我是不理解这个状态机,这个一个描述状态转移的东西吗?不懂,鸽。

期望dpP4316 绿豆蛙的归宿 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 

//反着——反向拓扑
#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 2e6 + 50;
int tot = 0;
int head[N];
double f[N];
int du[N], in[N];
int n, m;

struct node {
	int nex, to, val;
} arr[N];

void add(int u, int v, int c) {
	arr[++tot].nex = head[u];
	arr[tot].to = v;
	arr[tot].val = c;
	head[u] = tot;
}

void topu() {
	queueq;
	q.push(n);
	while (!q.empty()) {
		//cout << "111\n";
		int t = q.front();
		q.pop();
		for (int i = head[t]; i; i = arr[i].nex) {
			int v = arr[i].to;
			f[v] += (f[t] + arr[i].val) / du[v];
			//算期望,从v走到u对答案的贡献是(u点贡献+路径长)/从v到u的可能
			in[v]--;
			//因为是反向建图所以入度减掉
			if (!in[v]) {
				q.push(v);
			}
		}
	}
	//cout << "111\n";
}

void work() {
	mem(f, 0);
	mem(head, 0);
	mem(du, 0);
	mem(in, 0);
	cin >> n >> m;
	for (int i = 1; i <= m; ++i) {
		int a, b, c;
		cin >> a >> b >> c;
		add(b, a, c);    //建反图
		du[a]++;
		in[a]++;
	}
	f[n] = 0;
	topu();
	printf("%.2lf\n", f[1]);
}

signed main() {
	io;
	work();
	return 0;
}
//正着——跑dfs
#include 
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 2e6 + 50;
int tot = 0;
int head[N];
double f[N];
int du[N];
bool vis[N];
int n, m;

struct node {
	int nex, to, val;
} arr[N];

void add(int u, int v, int c) {
	arr[++tot].nex = head[u];
	arr[tot].to = v;
	arr[tot].val = c;
	head[u] = tot;
}

void dfs(int t) {
	if (vis[t])
		return;
	vis[t] = 1;
	for (int i = head[t]; i; i = arr[i].nex) {
		int v = arr[i].to;
		dfs(v);            //一定要先递归
		f[t] += (f[v] + arr[i].val);
        //状态转移的方程是一样的
	}
	if (du[t]) {            //要判断一下是否有度,不然可能出现/0的情况
		f[t] /= du[t];
	}
}

void work() {
	mem(f, 0);
	mem(head, 0);
	mem(du, 0);
	mem(vis, 0);
	cin >> n >> m;
	for (int i = 1; i <= m; ++i) {
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
		du[a]++;
	}
	dfs(1);
	printf("%.2lf\n", f[1]);
}

signed main() {
	io;
	work();
	return 0;
}

ps:看题解的时候看到了一篇博客,写的时候博主初二,初二是一个还没学期望就已经会了期望dp的年纪,膜膜oi✌别太荒谬。

小小总结

原来B题正解是二分啊乐,我以为我手绘了那么久的图推出来的规律必然符合数学场的正解,我赛时无法自然地想到并且写对二分,还是乖乖找规律。G我觉得我想的dfs很好,搜的没那么那么爆,但是我写了依托答辩。还有计算几何,虽然这题就是平面几何搞诈骗但是如果真出了寄几的签我是不会写(指没有能力)也不会写(指没有意愿)的。

刚补完,群友锐评质量不佳不如刷刷cf,乐。

jiangly不说话了呜呜难过,想听他超级大脑的巧思,不然我是无法看懂他的代(思)码(维)的。

新年快乐,会幸福。

你可能感兴趣的:(牛客,c++,算法)