[模拟赛]2022.07.28

难度:提高组

得分:40+20+0+100


[搜索]T1 计算器

给定 n n n 个装备,每个装备有属性 o p , a , b , c , d op,a,b,c,d op,a,b,c,d ,每个不同 o p op op 选最多一个,最大化
( ∑ a + 100 ) × ( ∑ b + 100 ) × ( ∑ c + 100 ) × ( ∑ d + 100 ) (\sum a+100)\times(\sum b+100)\times(\sum c+100)\times(\sum d+100) (a+100)×(b+100)×(c+100)×(d+100)

n ≤ 50 n\leq 50 n50


赛时看出来是搜索了,粗略算了一下,以为爆搜复杂度最大是 2 n 2 2^{\frac{n}{2}} 22n ,再加上时限3s,以为稳了,结果 100 − > 40 100->40 100>40 。算一下爆搜的实际复杂度,最大是 3 n 3 3^{\frac{n}{3}} 33n ,其实本来是可以接受的,但是却因为少了一个剪枝爆了复杂度。复杂度又算错了!若在前 n 3 \frac{n}{3} 3n 层结束了枚举,那么接下来最大是 2 n 3 \frac{2n}{3} 32n 层的空层,若跑满,复杂度退化为 3 n 3 × 2 n 3 3^{\frac{n}{3}}\times \frac{2n}{3} 33n×32n 。为了防止这种复杂度的退化,需要加入 n e x t next next 指针,直接跳到下一个不为空的地方,然后就跑得飞快了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct IO{
    inline char read(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template <typename _Tp> inline IO & operator >> (_Tp&x){
        static char c11,boo;
        for(c11=read(),boo=0;!isdigit(c11);c11=read()){
            if(c11==-1)return *this;
            boo|=c11=='-';
        }
        for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
        boo&&(x=-x);
        return *this;
    }
    inline void push(const char &c) {
    	putchar(c);
	}
	template <class T>
	inline void write(T x){
		if (x < 0) x = -x, push('-');
		static T sta[35];
		T top = 0;
		do {
			sta[top++] = x % 10, x /= 10;
		} while (x);
		while (top) push(sta[--top] + '0');
	}
	template <class T>
	inline void write(T x, char lastChar){
		write(x),push(lastChar);
	}
}io;
int n,k,ans;
struct node{
	int a,b,c,d;
	node(int _a=0,int _b=0,int _c=0,int _d=0){
		a=_a,b=_b,c=_c,d=_d;
	}
};

struct tp{
	node p[55];
	int pcnt;
	tp(){
		pcnt=0;
	}
}t[55];
int nxt[55];
void dfs(int dep,int _a,int _b,int _c,int _d){
	if(dep==k+1){
		ans=max(ans,(100ll+_a)*(100ll+_b)*(100ll+_c)*(100ll+_d));
		return;
	}
	for(int i=1;i<=t[dep].pcnt;++i){
		dfs(nxt[dep],_a+t[dep].p[i].a,_b+t[dep].p[i].b,_c+t[dep].p[i].c,_d+t[dep].p[i].d);
	}
	if(t[dep].pcnt==0)
		dfs(nxt[dep],_a,_b,_c,_d);
}


signed main(){
	io>>n>>k;
	for(int i=1;i<=n;++i){
		int tpi,ai,bi,ci,di;
		io>>tpi>>ai>>bi>>ci>>di;
		int j=++t[tpi].pcnt;
		t[tpi].p[j]=node(ai,bi,ci,di);
	}
	nxt[k]=k+1; 
	for(int i=k;i>=1;--i){
		if(t[i].pcnt==0)
			nxt[i-1]=nxt[i];
		else
			nxt[i-1]=i;
	}
	dfs(nxt[0],0,0,0,0);
	io.write(ans,'\n');
	return 0;
}

[计算几何,KMP]T2 对称轴

给定一个 n n n 边形,计算对称轴数量。多组数据, T ≤ 10 , n ≤ 1 0 5 T\leq 10,n\leq 10^5 T10,n105


我真没想到计算几何能和KMP,Manachar扯上关系。朴素做法 n 2 n^2 n2 枚举所有点和边,20分。

然后想改进做法,如果把点和边拆开看,一个多边形就相当于一个长为 2 n 2n 2n 的环,然后把环拆开,扩一倍,就得到长为 4 n 4n 4n 的链,在链上跑Manachar,若有某处的回文半径超过了 n n n (即回文长度超过了整个环),就计算一次答案。又或者跑KMP,在原串的反串上跑,只要长度等于 n n n 即为答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100005
#define ll long long
#define pll pair<ll, ll>
using namespace std;
struct IO{
    inline char read(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template <typename _Tp> inline IO & operator >> (_Tp&x){
        static char c11,boo;
        for(c11=read(),boo=0;!isdigit(c11);c11=read()){
            if(c11==-1)return *this;
            boo|=c11=='-';
        }
        for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
        boo&&(x=-x);
        return *this;
    }
    inline void push(const char &c) {
    	putchar(c);
	}
	template <class T>
	inline void write(T x){
		if (x < 0) x = -x, push('-');
		static T sta[35];
		T top = 0;
		do {
			sta[top++] = x % 10, x /= 10;
		} while (x);
		while (top) push(sta[--top] + '0');
	}
	template <class T>
	inline void write(T x, char lastChar){
		write(x),push(lastChar);
	}
}io;
 
int n,T;
struct point {
	ll x,y;
	point(ll xx=0, ll yy=0) {x=xx;y=yy;}
	friend point operator +(point a,point b) {
		return point(a.x+b.x,a.y+b.y);
	}
	friend point operator -(point a,point b) {
		return point(a.x-b.x,a.y-b.y);
	}
}a[maxn];
ll dot(point a,point b) {
	return a.x*b.x+a.y*b.y;
}
ll cross(point a,point b) {
	return a.x*b.y-a.y*b.x;
}
 
ll dist(point a,point b) {
	point v=a-b;
	return dot(v,v);
}
 
pll work_edge(int x) {
	int y=x+1;
	if(y>n) y=1;
	return pll(dist(a[x],a[y]),1e18);
}
pll work_ang(int x) { 
	int y=x+1,z=x+2;
	if(y>n) y=y%n;
	if(z>n) z=z%n;
	return pll(cross(a[y]-a[x],a[z]-a[y]),dot(a[y]-a[x],a[z]-a[y]));
}
pll edge[maxn*4],ang[maxn*4],s[maxn*4],t[maxn*4];
int nxt[maxn*4],f[maxn*4];
int KMP(int n,int m){
	nxt[1]=0;
	for(int i=2,j=0;i<=n;i++){
		while(j>0 && t[i]!=t[j+1])
			j=nxt[j];
		if(t[i]==t[j+1])
			j++;
		nxt[i]=j;
	}
	for(int i=2,j=0;i<=m;i++){
		while(j>0 && s[i]!=t[j+1])
			j=nxt[j];
		if(s[i]==t[j+1])
			j++;
		f[i]=j;
	}
	int cnt=0;
	for(int i=1;i<=m;i++) {
		if(f[i]==n)
			cnt++;
	}
	return cnt;
}
int main(){
	io>>T;
	while(T--) {
		io>>n;
		for(int i=1;i<=n;i++){
			int x,y;
			io>>x>>y;
			a[i].x=x;
			a[i].y=y;
		}
		for(int i=1;i<=n;i++) {
			edge[i]=work_edge(i);
			ang[i]=work_ang(i);
		}
		int newn=0;
		int newm=0;
		for(int i=1;i<=n;i++){
			s[++newn]=edge[i];
			s[++newn]=ang[i];
		}
		for(int i=1; i<=n; i++) {
			s[++newn]=edge[i];
			s[++newn]=ang[i];
		}
		for(int i=n*2; i>=1; i--) {
			t[++newm]=s[i];
		}
		printf("%d\n",KMP(newm,newn));
	}
}

[莫反]T3 互质

给定序列 a 1 , n a_{1,n} a1,n ,在线查询 m m m [ l , r ] [l,r] [l,r] 中与 x x x 互质的数。 n , m ≤ 1 0 5 n,m\leq 10^5 n,m105


[构造,猜结论]T4 签到题

给定 n + m n+m n+m 个点的二分图,在边上涂色,有 c c c 种颜色,不限数量。每个点的权值定义为边上颜色最多的数量减去边上颜色最少的数量,求最小权值。 n + m ≤ 1 0 6 n+m\leq 10^6 n+m106


名字不对劲,数据范围更不对劲,所以试图猜结论…然后就猜出来了…就是硬贪心,设边数为 p p p a n s = ∑ [ c    m o d    p    ! = 0 ] ans=\sum[c\,\,mod\,\,p\,\,!=0] ans=[cmodp!=0]

#include<bits/stdc++.h>
using namespace std;
struct IO{
    inline char read(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template <typename _Tp> inline IO & operator >> (_Tp&x){
        static char c11,boo;
        for(c11=read(),boo=0;!isdigit(c11);c11=read()){
            if(c11==-1)return *this;
            boo|=c11=='-';
        }
        for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
        boo&&(x=-x);
        return *this;
    }
    inline void push(const char &c) {
    	putchar(c);
	}
	template <class T>
	inline void write(T x){
		if (x < 0) x = -x, push('-');
		static T sta[35];
		T top = 0;
		do {
			sta[top++] = x % 10, x /= 10;
		} while (x);
		while (top) push(sta[--top] + '0');
	}
	template <class T>
	inline void write(T x, char lastChar){
		write(x),push(lastChar);
	}
}io;

int n,m,k,c;
int ans[1000005],ret;
int hs(int x){
	return x+n;
}
int main(){
	io>>n>>m>>k>>c;
	if(c==1){
		io.write(0,'\n');
		return 0;
	}
	for(int i=1;i<=k;++i){
		int u,v;
		io>>u>>v;
		ans[u]++,ans[hs(v)]++;
	}
	for(int i=1;i<=n+m;++i){
		ret+=(ans[i]%c)?1:0;
	}
	io.write(ret,'\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值