Codeforces #458 (Div1 + Div 2)

由于不是修仙党看见晚上12点开还3个小时就没打,后来线下开虚拟赛打的...估计现场打能涨不少分?

不过现场状态会更好还是更坏也说不定

A. Perfect Squares

题意: 求给定集合中最大的完全平方数 n<=1000 |ai|<=1000000

Sol:签到,我硬是没看见有负数WA了一发

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000009;

int vis[maxn];
int n,ans=-10000000;

int main()
{
	cin>>n;
	for(int i=0;i<=1000;i++) vis[i*i]=1;
	for(int i=1;i<=n;i++)
	{
		int x;cin>>x;
		if(x<0) ans=max(ans,x);
		else if(!vis[x]) ans=max(ans,x);
	}
	cout<<ans<<endl;
	return 0;
}


B. Conan and Agasa play a Card Game

题意:n张纸牌上有数,Alice柯南和Morisa\Bob阿笠博士轮流操作,每次选一张牌,把所有严格<这张牌的牌和他本身拿走,无法操作输,问先手是否必胜

Sol:如果最大的有奇数张,先手胜,否则谁先最大的谁输,可以转化为去掉最大值后的子游戏,有奇数先手必胜,否则后手胜

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000009;

int cnt[maxn],a[maxn];
int n;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),cnt[a[i]]++;
	bool flag=0;
	for(int i=100000;i>=1;i--) if(cnt[i]%2) flag=1;
	if(flag) puts("Conan");
	else puts("Agasa");
	return 0;
}


C. Travelling Salesman and Special Numbers

题意:定义f(x)=|x二进制位中1的个数|,n以内操作恰好k次变成1的数的个数 n<=2^1000 k <=1000

Sol:先dp出来1000以内每个数几次会变成1,规定1进行0次操作变成1,设为g(x),枚举1000以内g(x)==k-1的数,转化为求n以内二进制位有x个1的数的个数,经典数位DP问题,需要特判k=0和k=1

Code:

#include<bits/stdc++.h>
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int maxn = 1009;
const int mod = 1e9+7;

int f[maxn],a[maxn],C[maxn][maxn];
int n,m,ans,tot;
char str[maxn];

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int calc(int x)
{
	int res=0;
	while(x){if(x&1)res++;x>>=1;}
	return res;
}

void work(int x)
{
	int cnt=0;
	for(int i=n;i>=1;i--)
	{
		if(a[i])
		{
			if(x>=cnt) ans=(ans+C[i-1][x-cnt])%mod;
			cnt++;
		}
	}
	if(x==tot) ans=(ans+1)%mod;
}

int main()
{
	scanf("%s%d",str+1,&m);n=strlen(str+1);
	for(int i=1;i<=n;i++) a[i]=str[i]-'0',tot+=a[i];
	reverse(a+1,a+1+n);
	for(int i=0;i<=n;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	f[1]=1;
	for(int i=2;i<=1000;i++) f[i]=f[calc(i)]+1;
	if(m==0) puts("1");
	else
	{
		for(int i=1;i<=1000;i++) if(f[i]==m) work(i);
		printf("%d\n",(ans-(m==1)+mod)%mod);
	}
	return 0;
}


D. Bash and a Tough Math Puzzle

题意:给定正整数序列,两种操作:

1.输入l,r,k 询问区间[l,r]是否可以最多改变一个数使gcd==k

2.输入x,y 讲a[x]改成y

Sol:可以转化为区间中多少个数是k的倍数----不可做

但是最多1个数不是就可以GG了,维护区间gcd,当一个范围内大区间gcd不是k的倍数意味着里面肯定至少有一个数不是,暴力递归下去找,最多暴力找2次就没了,复杂度O(nlog^2n)

Code:

#include<bits/stdc++.h>
#define debug(x) cout<<#x<<"="<<x<<endl
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int maxn = 500009;
const int mod = 1e9+7;

struct sg_tree{int val;}node[maxn<<2];
int n,m,q,cnt;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int gcd(int x,int y)
{
	if(y==0) return x;
	return gcd(y,x%y);
}

void update(int rt){node[rt].val=gcd(node[rt<<1].val,node[rt<<1|1].val);}
void build(int l,int r,int rt)
{
	if(l==r)
	{
		node[rt].val=read();
		return ;
	}int mid=(l+r)>>1;
	build(lson);build(rson);
	update(rt);
}
void insert(int l,int r,int rt,int pos,int delta)
{
	if(l==r)
	{
		node[rt].val=delta;
		return ;
	}int mid=(l+r)>>1;
	if(pos<=mid) insert(lson,pos,delta);
	else insert(rson,pos,delta);
	update(rt);
}
void query(int l,int r,int rt,int left,int right,int delta)
{
	if(cnt<0) return ;
	if(l==r)
	{
		if(node[rt].val%delta) cnt--;
		return ;
	}
	if(l==left&&right==r) if(node[rt].val%delta==0) return ;
	int mid=(l+r)>>1;
	if(right<=mid) query(lson,left,right,delta);
	else if(left>mid) query(rson,left,right,delta);
	else query(lson,left,mid,delta),query(rson,mid+1,right,delta);
}

int main()
{
	n=read();
	build(1,n,1);
	m=read();
	while(m--)
	{
		int opt=read();
		if(opt==1)
		{
			int l=read(),r=read(),x=read();
			cnt=1;
			query(1,n,1,l,r,x);
			if(cnt>=0) puts("YES");
			else puts("NO");
			
		}
		else
		{
			int x=read(),y=read();
			insert(1,n,1,x,y);
		}
	}
	return 0;
}


E. Palindromes in a Tree

题意:一棵树,点上有字符,一条路径是好的当路径上的字符可以搞成回文的,求经过每个点的好路径的个数 n<=200000 字符集a-t

Sol:数一数发现字符集只有20,而可以搞成回文意味着每个字符的出现次数最多只有一个奇数,考虑装压每个字母奇偶性,而树上路径问题一般考虑点分治,找到重心,现在计算经过重心的路径对每个点的贡献,用cnt[i]记录i状态的数量,先把子树信息统计进去,再枚举每个子树,先删掉这个子树信息,统计以每个点为结尾的好路径个数,因为经过就会算贡献所以要把子树信息上传,最后再把信息加回来,复杂度O(nlogn*σ)

Code:

#include<bits/stdc++.h>
#define debug(x) cout<<#x<<"="<<x<<endl
typedef long long ll;
using namespace std;
const int maxn = 200009;

int first[maxn],siz[maxn],W[maxn],a[maxn];
ll ans[maxn];
int cnt[1<<21],sta[maxn];
struct edg{int next,to;}e[maxn<<1];
bool vis[maxn];
int n,tot,rt,sum,e_sum;
char str[maxn];

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void add_edg(int x,int y)
{
	e_sum++;
	e[e_sum].next=first[x];
	first[x]=e_sum;
	e[e_sum].to=y;
}

ll calc(int x,int val)
{
	ll res=0;
	for(int i=1;i<=tot;i++) res+=cnt[val^sta[i]];
	return res;
}

void find(int x,int fa)
{
	siz[x]=1;W[x]=0;
	for(int i=first[x];i;i=e[i].next)
	{
		int w=e[i].to;
		if(w==fa||vis[w]) continue;
		find(w,x);
		siz[x]+=siz[w];
		W[x]=max(W[x],siz[w]);
	}W[x]=max(W[x],sum-siz[x]);
	if(W[x]<W[rt]) rt=x;
}
void add(int x,int fa,int val,int opt)
{
	val^=(1<<a[x]);
	cnt[val]+=opt;
	for(int i=first[x];i;i=e[i].next)
	{
		int w=e[i].to;
		if(w==fa||vis[w]) continue;
		add(w,x,val,opt);
	}
}
ll work(int x,int fa,int val)
{
	val^=(1<<a[x]);
	ll res=calc(x,val);
	for(int i=first[x];i;i=e[i].next)
	{
		int w=e[i].to;
		if(w==fa||vis[w]) continue;
		res+=work(w,x,val);
	}
	ans[x]+=res;
	return res;
}

void solve(int x)
{
	vis[x]=1;
	cnt[1<<a[x]]++;
	for(int i=first[x];i;i=e[i].next)
	{
		int w=e[i].to;
		if(vis[w]) continue;
		add(w,x,1<<a[x],1);
	}
	ll res=calc(rt,0);
	for(int i=first[x];i;i=e[i].next)
	{
		int w=e[i].to;
		if(vis[w]) continue;
		add(w,x,1<<a[x],-1);
		res+=work(w,x,0);
		add(w,x,1<<a[x],1);
	}
	ans[x]+=res/2;
	for(int i=first[x];i;i=e[i].next)
	{
		int w=e[i].to;
		if(vis[w]) continue;
		add(w,x,1<<a[x],-1);
	}
	cnt[1<<a[x]]--;
	for(int i=first[x];i;i=e[i].next)
	{
		int w=e[i].to;
		if(vis[w]) continue;
		sum=siz[w];rt=0;
		find(w,0);solve(rt);
	}
}

int main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		add_edg(x,y);add_edg(y,x);
	}
	scanf("%s",str+1);
	for(int i=1;i<=n;i++) a[i]=str[i]-'a';
	sta[++tot]=0;
	for(int i=0;i<20;i++) sta[++tot]=1<<i;
	rt=0;sum=n;W[0]=n+1;
	find(1,0);solve(rt);
	for(int i=1;i<=n;i++) printf("%I64d ",ans[i]+1);
	return 0;
}


F. Substrings in a String

题意:一个字符串,两种操作:

1.输入x,y 把ch[x]改成y

2.输入l,r,str 询问[l-r]中str出现多少次

Sol:一开始完全不会...都开始想FFT了

旁边大爷吼了一句:"诶我去这题bitset暴力能过"

嘿嘿嘿

又刷新了我对复杂度的认知上线

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100009;

bitset<maxn> c[30],uuz,ans;
int n,m;
char str[maxn],tmp[maxn];

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int main()
{
	scanf("%s",str+1);n=strlen(str+1);
	for(int i=1;i<=n;i++) c[str[i]-'a'][i]=1;
	for(int i=0;i<=n;i++) uuz[i]=1;
	m=read();
	while(m--)
	{
		int opt=read();
		if(opt==1)
		{
			int x=read();scanf("%s",tmp+1);
			c[str[x]-'a'][x]=0;
			c[tmp[1]-'a'][x]=1;
			str[x]=tmp[1];
		}
		else
		{
			int l=read(),r=read();scanf("%s",tmp+1);
			int len=strlen(tmp+1);
			if(len>r-l+1) puts("0");
			else
			{
				ans=uuz;
				for(int i=1;i<=len;i++) ans&=(c[tmp[i]-'a']>>i);
				printf("%d\n",(ans>>(l-1)).count()-(ans>>(r-len+1)).count());
			}
		}
	}
	return 0;
}


G. Sum the Fibonacci

题意:给一个序列求

  • 1 ≤ a, b, c, d, e ≤ n
  • (sa | sb) & sc & (sd ^ se) = 2i  
  • sa & sb = 0
f(sa | sb) * f(sc) * f(sd ^ se)     

f是斐波那契数列 i是任意自然数

的和mod 1e9+7

Sol: 没时间写了,,而且我也不会

后来知道是可以FWT硬上,条件3暴力枚举子集就行

等我会了FWT再回来补上...

学(bei)了FWT...

只考虑第二个条件,可以先求出cnt[x]表示x的个数,cor[x] 表示满足[sa|sb=x]的数对的数量,cxor[x]表示满足[sa^sb=x]的数对的数量

后两个可以用FWT做

然后把cor\cxor\cnt变成fib[cor\cxor\cnt]

再把这三个FWT起来,枚举i就可以了

考虑第三个条件,这是一个子集卷积(又不会)

但是可以3^log(n)暴力枚举子集,不做或卷积

复杂度O(3^log(n)+nlogn)

Code:

#include<bits/stdc++.h>
#define AND 1
#define OR 2
#define XOR 3
typedef long long ll;
using namespace std;
const int maxn = (1<<17)+9;
const ll mod = 1e9+7, inv = 5e8+4;

ll cnt[maxn],cor[maxn],cxor[maxn],fib[maxn],a[maxn],b[maxn];
int n,mr=maxn-9;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

inline ll MOD(ll x)
{
	x%=mod;
	if(x<0) x+=mod;
	return x;
}

void fwt(ll *x,int opt,int type)
{
	for(int i=1;i<mr;i<<=1)
		for(int p=i<<1,j=0;j<mr;j+=p)
			for(int k=0;k<i;k++)
			{
				ll L=x[j+k],R=x[j+k+i];
				if(opt==AND) x[j+k]=MOD(L+R*type);
				if(opt==OR) x[j+k+i]=MOD(R+L*type);
				if(opt==XOR)
				{
					x[j+k]=MOD(L+R);x[j+k+i]=MOD(L-R);
					if(type==-1) x[j+k]=x[j+k]*inv%mod,x[j+k+i]=x[j+k+i]*inv%mod;
				}
			}
}

int main()
{
	n=read();
	fib[1]=1;
	for(int i=2;i<=mr;i++) fib[i]=MOD(fib[i-1]+fib[i-2]);
	for(int i=1;i<=n;i++)
	{
		int x=read();
		cnt[x]++;
	}
	for(int i=0;i<=mr;i++)
		if(cnt[i])
		{
			int s=(mr-1)-i;
			for(int j=s;;j=(j-1)&s)
			{
				cor[i|j]=MOD(cor[i|j]+1ll*cnt[i]*cnt[j]);
				if(j==0) break;
			}
		}
	memcpy(cxor,cnt,sizeof cxor);
	fwt(cxor,XOR,1);
	for(int i=0;i<=mr;i++) cxor[i]=MOD(cxor[i]*cxor[i]);
	fwt(cxor,XOR,-1);
	for(int i=0;i<=mr;i++)
	{
		cnt[i]=MOD(cnt[i]*fib[i]);
		cor[i]=MOD(cor[i]*fib[i]);
		cxor[i]=MOD(cxor[i]*fib[i]);
	}
	fwt(cnt,AND,1);fwt(cor,AND,1);fwt(cxor,AND,1);
	for(int i=0;i<=mr;i++) cnt[i]=cnt[i]*cor[i]%mod*cxor[i]%mod;
	fwt(cnt,AND,-1);
	ll ans=0;
	for(int i=0;i<=16;i++) ans=MOD(ans+cnt[1<<i]);
	cout<<ans<<endl;
	return 0;
}

最终成绩rk33 还行呀~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值