第四届辽宁省大学生程序设计竞赛(正式赛)(9/13/13)

AC情况
赛中通过赛后通过暂未通过
A
B
C
D
E
F
G
H
I
J
K
L
M
整体体验

easy:ABFHL

mid:MJGC

hard:IDKE

心得

感觉出了一堆典题,少数题还有些意思,E题确实神仙

题解
A. 欢迎来到辽宁省赛(签到)

输出27

B. 胜率(枚举)

枚举分母到10000

// Problem: 胜率
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/B
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
int t,a,b;
void sol(){
	scanf("%d.%d",&a,&b);
	a=a*100+b;
	//printf("a:%d\n",a);
	//printf("%d\n",30000/7);
	rep(i,1,10000){
		rep(j,0,i){
			int v=j*10000/i,w=(j*100000/i)%10;
			if(w>=5)v++;
			if(v==a){
				pte(i);
				return;
			}
		}
	}
}
int main(){
	t=1;
	while(t--){
		sol();
	}
	return 0;
}
F. 隔板与水槽(枚举)

枚举一下中间的隔板

// Problem: 隔板与水槽
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/F
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=5e3+10;
int t,n,a[N];
void sol(){
	sci(n);
	rep(i,1,n)sci(a[i]);
	ll ans=0;
	rep(i,2,n-1){
		ll ma=0,mb=0;
		rep(j,1,i-1){
			ma=max(ma,1ll*min(a[i],a[j])*(i-j));
		}
		rep(j,i+1,n){
			mb=max(mb,1ll*min(a[i],a[j])*(j-i));
		}
		ans=max(ans,ma+mb);
	}
	ptlle(ans);
}
int main(){
	t=1;
	while(t--){
		sol();
	}
	return 0;
}
H. 取石子(博弈 性质题)

eq?a%5E2x%5E2&plus;ax&plus;1eq?b%5E2y%5E2&plus;by&plus;1都是奇数,所以只和初始局面奇偶性有关

// Problem: 取石子
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/H
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
int t,a,b,n;
void sol(){
	sci(a),sci(b),sci(n);
	puts(n&1?"Alice":"Bob");
}
int main(){
	sci(t); // t=1
	while(t--){
		sol();
	}
	return 0;
}
L. 区间与绝对值(莫队+树状数组)

莫队+树状数组维护增加/删除时对应贡献改变即可

n=q=1e5,4s,O(nsqrt(n)logn)还可以

// Problem: 区间与绝对值
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/L
// Memory Limit: 524288 MB
// Time Limit: 8000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int maxn=1e5+10,N=maxn;
int n,m,l,r,a[maxn];
int col[maxn];
int pos[maxn];//pos[i]代表i下标所在的块号
ll now[maxn];//now[i]代表颜色i有几只袜子 
ll res,len,dom,g;
int sz;//块的大小
struct node
{
	int l,r,id;
	ll ans;
}e[maxn];
bool cmp1(node a,node b)
{
	if(pos[a.l]==pos[b.l])
    {
        if(pos[a.l]&1)return a.r>b.r;
        else return a.r<b.r;
    }
	return a.l<b.l;
}
bool cmp2(node a,node b)
{
	return a.id<b.id;
}

struct BitPre{ // 求前缀和(可改为max等)
	int n,tr[N];
	void init(int _n){
		n=_n;
		memset(tr,0,(n+1)*sizeof(*tr));
	}
	void addSum(int x,int v){
		for(int i=x;i<=n;i+=i&-i)
		tr[i]+=v;
	}
	void addMx(int x,int v){
		for(int i=x;i<=n;i+=i&-i)
		tr[i]=max(tr[i],v);
	}
	int askSum(int x){
		int ans=0; 
		for(int i=x;i;i-=i&-i)
		ans+=tr[i];
		return ans;
	}
    int askMx(int x){
		int ans=0; 
		for(int i=x;i;i-=i&-i)
		ans=max(ans,tr[i]);
		return ans;
	}
    // 树状数组求从小到大第k个, 1<=k<=sum(n), 1<=x<=n
    int kth(int k){
        int x=0;
        for(int i=1<<std::__lg(n);i;i>>=1){
            if(x+i<=n && k>tr[x+i]){
                x+=i;
                k-=tr[x];
            }
        }
        return x+1;
    }
}tr,tr2;
void add(int pos,int v){
	res-=tr.askSum(v);
	res+=1ll*tr2.askSum(v)*v;
	res+=tr.askSum(maxn-10)-tr.askSum(v);
	res-=(1ll*tr2.askSum(maxn-10)-tr2.askSum(v))*v;
	tr.addSum(v,v);tr2.addSum(v,1);
}
void del(int pos,int v){
	tr.addSum(v,-v);tr2.addSum(v,-1);
	res+=tr.askSum(v);
	res-=1ll*tr2.askSum(v)*v;
	res-=tr.askSum(maxn-10)-tr.askSum(v);
	res+=(1ll*tr2.askSum(maxn-10)-tr2.askSum(v))*v;
}
signed main(){
	sci(n),sci(m);
	tr.init(maxn-5);
	tr2.init(maxn-5);
	rep(i,1,n)sci(a[i]);
	sz=(int)sqrt(n); 
	for(int i=1;i<=n;++i)
	pos[i]=1+(i-1)/sz;
	for(int i=1;i<=m;++i){
		scanf("%d%d",&e[i].l,&e[i].r);
		e[i].id=i;
	}
	sort(e+1,e+m+1,cmp1);
	l=1;r=0;//[l,r] 初始什么都没有 
	for(int i=1;i<=m;++i){
		for(;r<e[i].r;r++)add(r+1,a[r+1]);
		for(;r>e[i].r;r--)del(r,a[r]);
		for(;l<e[i].l;l++)del(l,a[l]);
		for(;l>e[i].l;l--)add(l-1,a[l-1]);
		e[i].ans=res;
	}
	sort(e+1,e+m+1,cmp2);
	for(int i=1;i<=m;++i){
		printf("%lld\n",2ll*e[i].ans);
	}
	return 0;
}
M. 让二追三(概率期望)

n<5的情况输出0,n>=5的话,统计每个五连位置00111对答案个数的贡献,

有n-4个这样的五连位置,所以是(n-4)*p,其中p为出现00111的概率

// Problem: 让二追三
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/M
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=1e5+10,mod=1e9+7,inv2=(mod+1)/2;
int t,a,b,n;
int modpow(int x,int n,int mod){
	int res=1;
	for(;n;n>>=1,x=1ll*x*x%mod){
		if(n&1)res=1ll*res*x%mod;
	}
	return res;
}
void sol(){
	sci(a),sci(b),sci(n); 
    if(n<5){
        puts("0");
        return;
    }
	int p=1ll*a*modpow(b,mod-2,mod)%mod;
	int np=(1-p+mod)%mod;
	int w=1ll*p*p%mod*p%mod*np%mod*np%mod;
	pte(1ll*(n-4)*w%mod);
}
int main(){
	sci(t); // t=1
	while(t--){
		sol();
	}
	return 0;
}
J. 齐次递推公约数(反演 gcd fib性质)

类斐波那契数列性质,有f(gcd(i,j))=gcd(f(i),f(j))

所以只需要枚举gcd=d,统计gcd=d的(i,j)对数有多少对,

假设有x对,则对答案的贡献是f(d)*x

可以反演,这里用的是减掉d的倍数的方式,得到恰好等于d的方案数

// Problem: 齐次递推公约数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/J
// Memory Limit: 524288 MB
// Time Limit: 6000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)]
const int N=3e6+10,mod=1e9+7;
int t,n,f[N],res;
ll cnt[N];
void sol(){
	sci(n);
	f[1]=1;
	rep(i,2,n){
		f[i]=(3ll*f[i-1]%mod+2ll*f[i-2]%mod)%mod;
	}
	per(i,n,1){
		int tot=1;
		for(int j=2*i;j<=n;j+=i){
			cnt[i]-=cnt[j];
			tot++;
		}
		cnt[i]+=1ll*tot*tot;
		res=(res+1ll*cnt[i]%mod*f[i]%mod)%mod;
	}
	pte(res);
	// int res2=0;
	// for(int i=1;i<=n;++i){
		// for(int j=1;j<=n;++j){
			// res2=(res2+__gcd(f[i],f[j]))%mod;
		// }
	// }
	// pte(res2);
}
int main(){
	t=1;
	while(t--){
		sol();
	}
	return 0;
}
G. 树上公约数(树的直径 )

枚举gcd=d,将边权是d的倍数的边都连起来,得到对应森林

若森林里存在长度为k的路径,即森林的某棵树上存在长度>=k的直径

对于每棵树两遍搜索求直径,找到符合条件的最大的d,不存在输出-1

复杂度eq?O%28n*max%28d_%7Ba_%7Bn%7D%7D%29%29

527832b983bdefa820ffd86476b21b6b.jpeg

// Problem: 树上公约数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/G
// Memory Limit: 524288 MB
// Time Limit: 10000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=2e5+10,M=2e5;
int n,k,p[N],w,tmp,to;
vector<int>fac[N],e[N];
vector<int>edg[N];
bool vis[N];
void dfs(int u,int fa,int dis){
	vis[u]=1;
	if(dis>tmp)tmp=dis,to=u;
	for(auto &v:e[u]){
		if(v==fa)continue;
		dfs(v,u,dis+1);
	}
}
int sol(){
	per(i,M,1){
		if(!SZ(edg[i]))continue;
		for(auto &id:edg[i]){
			e[id].clear();
			e[p[id]].clear();
			vis[id]=0;
		}
		for(auto &id:edg[i]){
			e[id].pb(p[id]);
			e[p[id]].pb(id);
		}
		for(auto &id:edg[i]){
			if(vis[id])continue;
			tmp=1;
			dfs(id,0,1);
			tmp=1;
			dfs(to,0,1);
			if(tmp>=k)return i;
		}
	}
	return -1;
}
int main(){
	for(int i=1;i<=M;++i){
		for(int j=i;j<=M;j+=i){
			fac[j].pb(i);
		}
	}
	sci(n),sci(k);
	rep(i,2,n){
		sci(p[i]),sci(w);
		for(auto &d:fac[w]){
			edg[d].pb(i);
		}
	}
	pte(sol());
	return 0;
}
C. 连环爆炸(dp)

题目链接:https://ac.nowcoder.com/acm/contest/68774/C

这个题还挺有意思的,出的挺好的,虽然三个ac的代码都是贪心骗过去的

按怪的先死到后死排序,这样就是无后效性,所以按血量增序排序,

假设我们已经获得了一些预制伤害值就可以顺利触发流程炸死所有怪,

但是不知道这个值具体是多少,所以把这个值存入dp值

按血量增序排序,

dp[i][j]表示前i个怪,手动消灭了j个,令前i个怪全死,还需要在开局预置的伤害数最少是多少

转移就是枚举第i个怪是手动爆的,

还是被前面的崩死的,

还是没崩死,需要开局再补充delta伤害

// Problem: 连环爆炸
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/C
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=4e3+10;
int n;
P c[N];
ll sum[N],dp[N][N];//dp[i][j]表示前i个手动爆了j个 前i个全死 需要预支的伤害最小值
void ckmin(ll &x,ll y){
	x=min(x,y);
}
int main(){
	sci(n);
	rep(i,1,n)sci(c[i].fi),sci(c[i].se);
	sort(c+1,c+n+1);
	rep(i,1,n){
		rep(j,0,i){
			dp[i][j]=4e18;
		}
	}
	dp[1][0]=c[1].fi;
	dp[1][1]=0;
	sum[1]=c[1].se;
	rep(i,1,n-1){
		sum[i+1]=sum[i]+c[i+1].se;
		rep(j,0,i){
			if(sum[i]<c[i+1].fi){
				ckmin(dp[i+1][j],max(dp[i][j],c[i+1].fi-sum[i]));
			}
			else{
				ckmin(dp[i+1][j],dp[i][j]);
			}
			ckmin(dp[i+1][j+1],max(0ll,dp[i][j]-c[i+1].se));
		}
	}
	// rep(i,1,n){
		// rep(j,0,i){
			// printf("i:%d j:%d dp:%d\n",i,j,dp[i][j]);
		// }
	// }
	rep(i,0,n){
		if(dp[n][i]==0){
			pte(i);
			return 0;
		}
	}
	return 0;
}
补题部分
D. 三角形打野(计算几何 叉积 三分)
题意

824d1e30419f1bcf07e7c70c5d187e42.png
给定x轴正半轴,以及从原点出发的一条射线(在第一象限内),

以及两个点(保证这两个点在给定射线与x正半轴夹角范围内,而不在x轴或给定的射线上),

做一条直线AB,使得这条直线与x正半轴和给定射线组成的三角形将给定的两个点包含在内或边界上。

求满足条件的三角形的最小面积。

两点坐标可能相等,误差小于0.001即为正确

题解

太久不写计算几何感觉基础题都没什么思路,补的话感觉也只能用高中的思路写

手玩一下发现,所求直线AB和x轴的交点是在一个区间内,并且一定过其中至少一个点

记原来给的两个点为P、Q,作PX1平行射线交x轴于X1,作QX2平行射线交x轴于X2

左端点是X1和X2更靠右的那个,右端点可以设成一个很大的值,比如1e18

既然有当前x轴交点坐标,并且能取得最小值,所以凹函数,三分这个x的位置

将当前x轴交点X与P、Q分别连线,XP在XQ顺时针方向说明外侧是XP,否则是XQ

这个也画一下,就很显而易见

然后就是求外侧向量(不妨是XP)与射线的交点,求出交点后用叉积即可求得面积

代码
// Problem: 三角形打野
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/D
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
 
using i64 = long long;
using T = db;
const db eps = 1e-8;
 
struct Point {
    T x;
    T y;
    Point(T x = 0, T y = 0) : x(x), y(y) {}
    
    Point &operator+=(const Point &p) {
        x += p.x, y += p.y;
        return *this;
    }
    Point &operator-=(const Point &p) {
        x -= p.x, y -= p.y;
        return *this;
    }
    Point &operator*=(const T &v) {
        x *= v, y *= v;
        return *this;
    }
    friend Point operator-(const Point &p) {
        return Point(-p.x, -p.y);
    }
    friend Point operator+(Point lhs, const Point &rhs) {
        return lhs += rhs;
    }
    friend Point operator-(Point lhs, const Point &rhs) {
        return lhs -= rhs;
    }
    friend Point operator*(Point lhs, const T &rhs) {
        return lhs *= rhs;
    }
};
 
T dot(const Point &a, const Point &b) {
    return a.x * b.x + a.y * b.y;
}
 
T cross(const Point &a, const Point &b) {
    return a.x * b.y - a.y * b.x;
}
int t;
db k,x,y;
Point a,b;
db pos(db k1,db x,db y){
	db b=y-k1*x;
	return -b/k1;
}
db cal(db z){
	Point p(z,0),o(0,0);
	Point pa=a-p,pb=b-p,pc,po=o-p;
	if(cross(pa,pb)>0)pc=pa;
	else pc=pb;
	if(fabs(pc.x)<eps){
		return 0.5*z*k*z;
	}
	db xs=k*z/(pc.y-k*pc.x);
	Point up(z+xs*pc.x,xs*pc.y);
	pc=up-p;
	db ans=0.5*fabs(cross(pc,po));
	return ans;
}
void sol(){
	scanf("%lf%lf%lf%lf%lf%lf",&y,&x,&a.x,&a.y,&b.x,&b.y);
	db l=0,r=1e18;
	k=y/x;
	l=max(l,pos(k,a.x,a.y));
	l=max(l,pos(k,b.x,b.y));
	while(r-l>eps){
		db m=(l+l+r)/3,m2=(l+r+r)/3;
		if(cal(m)<cal(m2))r=m2;
		else l=m;
	}
	printf("%.10lf\n",cal(l));
}
int main(){
	sci(t); // t=1
	while(t--){
		sol();
	}
	return 0;
}
I. 元-神(单调栈 单调队列)
题意

8d3f70b571b0b2bd44bca1878283eb10.png

7b6c1df9eec476b88e3f724d91091755.png

题解

写到这个题的时间有点不够,感觉再手玩玩就玩出来了,诈骗题

考虑暴力怎么做,最暴力当然是从左往右迭代eq?m%5E2

稍微不那么暴力一点的暴力,是从右到左维护第i个值按时间序都可能是哪些值,是一条链表

但是链表的复杂度也是最坏eq?O%28m%5E2%29的,

考虑最右的值只有一种,右数第二的值有两种,以此类推

如果拿第i个值暴力的和第i+1个值的链表上的值一个一个比,自然是eq?O%28m%5E2%29

称第i值为L,第i+1个值的链表上当前要比的值为R

1. 若c[L][R]=1,说明第i个值不会变成这个R,那么第i个值左侧的值有L的阻挡也不会变成R,可以把R从链表里删了

2. 若c[R][L]=1,说明第i个值是L的时候,会在这一轮被同化成R,那么把L加到R的前面,停止操作即可

由于比每次都是从链头比的,并且第i条链是从第i+1条链删掉若干次头结点后塞入元素L,

所以,实际是一个栈结构,单调栈维护即可,就是一个弹栈的过程,

弹到为空或出现第二种情况后,把L放入栈顶,此时栈底的元素就是经过若干轮后最终变成的元素

因为还要访问栈底,所以写的实际是个双端队列

代码
// Problem: 元-神
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/I
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=1e3+10,M=1e6+10;
int n,t,c[N][N],m,a[M];
int sol(){
	deque<int>q;
	sci(m);
	rep(i,1,m){
		sci(a[i]);
    }
    int ans=0;
    per(i,m,1){
    	while(!q.empty() && !c[q.front()][a[i]])q.pop_front();
    	q.push_front(a[i]);
    	ans^=q.back();
        //printf("%d ",q.back());
    }
	return ans;
}
int main(){
	sci(n);sci(t); // t=1
	rep(i,1,n){
		rep(j,1,n){
			sci(c[i][j]);
		}
	}
	while(t--){
		pte(sol());
	}
	return 0;
}
 K. 稻妻扑克(大模拟,2024.10.26)
题目

https://ac.nowcoder.com/acm/contest/94343/Kt

题解

大模拟,按题意模拟即可

把70张牌先映射成0-69,按照题目约束对有加成的组加上系数

对应第二种选择,也就是选一张没出现过的牌的时候,加个剪枝,

因为手里有两张牌,所以剩下一张牌要么是X10,要么是X9,剩下的牌不用考虑

注意到有五张牌ABCDE的时候,先打ABC再打DE和先打DE再打ABC实际是一样的,

这里我们强制先打牌数多的,也就是先打3,再打2,再打1,

这样一个合法序列就只会被搜到一次,不加这个剪枝就会T成sb

当然严格的说,对于ABCD这种序列,先AB后CD和先CD后AB还是会搜两次,

但是已经减少了很多无效搜索了

另一个想法是,用二进制11111表示这5个数,

于是取1个数/2个数/3个数也就是得到当前数的子集,

用子集状态S和当前状态i差的二进制的位数去控制,

做一下子集dp即可,有点大炮轰蚊子了

代码(搜索)
#include<bits/stdc++.h>
using namespace std;
#define db double
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
typedef long long ll;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
const db eps=1e-6;
char s[5];
vector<int>a,b,c;
db mp[7][7];
bool vis[70];
int t;
db my,you;
P f(int v){return P(v/10,(v%10)+1);}
db calc(vector<int>x){
    sort(x.begin(),x.end());
    vector<int>y;
    db sum=0;
    for(auto &v:x){
        P z=f(v);
        y.pb(z.fi);
        sum+=z.se;
    }
    if(SZ(x)==2)return mp[y[0]][y[1]]*sum;
    else{
        if(y[2]==6)return mp[y[0]][y[1]]*sum;
        vector<int>u={0,1,2},v={0,2,4};
        if(y==u || y==v)return 4*sum;
        return -1;
    }
}
void sol(vector<int>x,db &ans,db now,int up){
    int sz=SZ(x);
    rep(i,0,sz-1){
        rep(j,i+1,sz-1){
            rep(k,(up>=3?j+1:sz),sz){
                vector<int>vec;
                if(k==sz)vec={x[i],x[j]};
                else vec={x[i],x[j],x[k]};
                db z=calc(vec);
                if(z<=0)continue;
                vector<int>y;
                rep(l,0,sz-1){
                    if(l!=i && l!=j && l!=k){
                        y.pb(x[l]);
                    }
                    if(SZ(y))sol(y,ans,now+z,2);
                    else ans=max(ans,now+z);
                }
            }
        }
    }
    for(auto &v:x){
        now+=f(v).se;
    }
    ans=max(ans,now);
}
void cal(vector<int>&x,vector<int>&y,db &ans){
    int sz=SZ(y);
    rep(i,0,sz-1){
        rep(j,i+1,sz-1){
            rep(k,j+1,sz-1){
                vector<int>p=x;
                p.pb(y[i]),p.pb(y[j]),p.pb(y[k]);
                sol(p,ans,0,3);
            }
        }
    }
    rep(i,0,69){
        if(!vis[i] && (i%10)>7){
            vector<int>p=x;
            p.pb(i);
            sol(p,ans,0,3);
        }
    }
}
int main(){
    mp[0][1]=mp[0][2]=mp[2][4]=2;
    mp[0][3]=mp[0][4]=mp[1][4]=mp[1][2]=mp[3][4]=1.5;
    mp[1][3]=3;
    sci(t);
    while(t--){
        a.clear(),b.clear(),c.clear();
        my=you=0;
        memset(vis,0,sizeof vis);
        rep(i,0,8){
            scanf("%s",s);
            int v=(s[0]-'A')*10+((strlen(s)>=3)?9:(s[1]-'1'));
            if(i<=1)a.pb(v);
            else if(i<=3)b.pb(v);
            else c.pb(v);
            vis[v]=1;
        }
        cal(a,c,my);
        cal(b,c,you);
        printf("%.2lf %.2lf\n",my,you);
        puts(my>you+eps?"Win!":"Lose!");
    }
    return 0;
}
代码2(状压)
#include<bits/stdc++.h>
using namespace std;
#define db double
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
typedef long long ll;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
const db eps=1e-6;
char s[5];
vector<int>a,b,c,can[35];
db mp[7][7];
bool vis[70];
int t;
db my,you;
P f(int v){return P(v/10,(v%10)+1);}
db calc(vector<int>p,int q,int r){
    vector<int>x;
    rep(i,0,q-1){
        if(r>>i&1)x.pb(p[i]);
    }
    if(SZ(x)==1)return f(x[0]).se;
    sort(x.begin(),x.end());
    vector<int>y;
    db sum=0;
    for(auto &v:x){
        P z=f(v);
        y.pb(z.fi);
        sum+=z.se;
    }
    if(SZ(x)==2)return mp[y[0]][y[1]]*sum;
    else{
        if(y[2]==6)return mp[y[0]][y[1]]*sum;
        vector<int>u={0,1,2},v={0,2,4};
        if(y==u || y==v)return 4*sum;
        return -1;
    }
}
void sol(vector<int>x,db &ans){
    int n=x.size(),up=(1<<n)-1;
    vector<db>dp(up+1,0);
    rep(i,1,up){
        if(__builtin_popcount(i)<=3)dp[i]=calc(x,n,i);
        for(int S=i;S;S=(S-1)&i){
            dp[i]=max(dp[i],dp[S]+dp[i^S]);
        }
    }
    ans=max(ans,dp[up]);
}
void cal(vector<int>&x,vector<int>&y,db &ans){
    int sz=SZ(y);
    rep(i,0,sz-1){
        rep(j,i+1,sz-1){
            rep(k,j+1,sz-1){
                vector<int>p=x;
                p.pb(y[i]),p.pb(y[j]),p.pb(y[k]);
                sol(p,ans);
            }
        }
    }
    rep(i,0,69){
        if(!vis[i] && (i%10)>7){
            vector<int>p=x;
            p.pb(i);
            sol(p,ans);
        }
    }
}
int main(){
    mp[0][1]=mp[0][2]=mp[2][4]=2;
    mp[0][3]=mp[0][4]=mp[1][4]=mp[1][2]=mp[3][4]=1.5;
    mp[1][3]=3;
    sci(t);
    while(t--){
        a.clear(),b.clear(),c.clear();
        my=you=0;
        memset(vis,0,sizeof vis);
        rep(i,0,8){
            scanf("%s",s);
            int v=(s[0]-'A')*10+((strlen(s)>=3)?9:(s[1]-'1'));
            if(i<=1)a.pb(v);
            else if(i<=3)b.pb(v);
            else c.pb(v);
            vis[v]=1;
        }
        cal(a,c,my);
        cal(b,c,you);
        printf("%.2lf %.2lf\n",my,you);
        puts(my>you+eps?"Win!":"Lose!");
    }
    return 0;
}
E. 神-原(lucas定理 递归 极值点)
题目

51c3bb7eca0d715f6a62d3192b418868.png

思路来源

2023辽宁省赛E-优快云博客

题解

纯纯神仙题,完全根据思路来源补的代码

虽然lucas的部分是暴力展开的,但是我极值取到的[l,r]就没求出来

虽然理论复杂度在p=2这是1e7左右,但是写的时候是瓶颈,实际提交的过程中一度TLE

最后想起来,p=2的时候,lucas定理的推论,n&m=m时C(n,m)%p=1,

加上之后就AC了,那一刻,真香!

代码
// Problem: 神-原
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/68774/E
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef unsigned long long ull;
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=1e6+10,M=1e6,H=2e7+10;
int n,m,p,mod;
int Finv[N],fib[N],fac[N],inv[N];
int f(int a,int n);
int g(int a,int n);
int modpow(int x,int n,int mod){
	int res=1;
	for(;n;x=1ll*x*x%mod,n>>=1)
	if(n&1)res=1ll*res*x%mod;
	return res;
}
void init(int n){ //n<N
    inv[0]=inv[1]=1;
    fib[0]=0;fib[1]=1;
    rep(i,2,n)fib[i]=(fib[i-1]+fib[i-2])%mod;
    for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	fac[0]=Finv[0]=1;
	for(int i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod,Finv[i]=1ll*Finv[i-1]*inv[i]%mod;
}
int C(int n,int m){
	if(m<0||m>n)return 0;
    if(p==2)return (n&m)==m?1:0;
	int ans=1ll*fac[n]*Finv[n-m]%mod*Finv[m]%mod;
	//printf("n:%d m:%d c:%d\n",n,m,ans);
	return ans;
}
int Lucas(int n, int m){
  if(m<0||m>n)return 0;
  if(p==2)return (n&m)==m?1:0;
  return m==0?1:1ll*C(n%p,m%p)*Lucas(n/p,m/p)%p;
}
int g(int a,int n){
    //if(a<0 || n<0)return 0;
    int x=n%p,y=n/p,z=a/p;
	int v=1ll*f(x,x)*f(z-1,y)%p;
	if(p-1==x)return v;
	int w=(f(p-1,x+p)-f(x,x+p)+p)%p;
    if(w){
        int u=f(z-1,y-1);
	    v=(v+1ll*w*u%p)%p;
    }
	return v;
}
int ch(int a,int n){
	int up=a%p,ans=0;
	rep(i,0,up){
		int l=C((n-i)%p,i),r=Lucas((n-i)/p-a/p,a/p);
		ans=(ans+1ll*l*r%p)%p;
	}
	return ans;
}
int f(int a,int n){
    if(a<0 || n<0)return 0;
    if(a==n && n<M)return fib[n+1];
	a=min(a,n);
    if(n<=p){
        int ans=0;
		rep(i,0,a){
			ans=(ans+C(n-i,i))%mod;
		}
		//printf("a:%d n:%d ans:%d\n",a,n,ans);
		return ans;
    }
	if(a<=M){
		int ans=0;
		rep(i,0,a){
			ans=(ans+Lucas(n-i,i))%mod;
		}
		//printf("a:%d n:%d ans:%d\n",a,n,ans);
		return ans;
	}
	return (g(a,n)+ch(a,n))%p;
}
int sol(){
	if(!m)return f(n-1,n-1);
    if(m==n)return 0;
	int l=(n-2*m+2)/3+1,r=l+m-1;
	if(l<1)l=1,r=m;
	//printf("l:%d r:%d f(n-1,n-1):%d\n",l,r,f(n-1,n-1));
	int v=(f(n-1,n-1)-f(r-1,n-1)+p)%p;
	if(l>1)v=(v+f(l-2,n-m-1))%p;
	return v;
}
int main(){
	sci(n),sci(m),sci(p);
	mod=p;
	init(M);
	pte(sol());
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值