AtCoder Regular Contest 099

AtCoder Regular Contest 099


 

C - Minimization

题意:给出一个n的排列。每次操作可以使一段长度为K的连续子序列变成该序列的最小数。

求最少几次使得

分析:枚举包含1的序列是哪个,然后左右O(1)求答案。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 100050
int a[N],n,k,pos;
int main() {
	scanf("%d%d",&n,&k);
	int i;
	for(i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		if(a[i]==1) pos=i;
	}
	int ans=1<<30;
	for(i=max(1,pos-k+1);i<=pos;i++) {
		int j=i+k-1;
		ans=min(ans,(i-1+k-2)/(k-1)+(n-j+k-2)/(k-1));
	}
	printf("%d\n",ans+1);
}

 

D - Snuke Numbers

题意:令S(n)表示n这个数各位之和。定义一个数n合法:所有m>n,都有n/S(n)<=m/S(m)。

输出前K个合法的数。

分析:显然所有99999这样的数都合法。

假设现在有一个数xyz999,这个数显然比xy9z99优,故在数字后面加的9的个数不降。

也就是是说每次加上$10^i$,其中i不降。需要判断这个数$+10^i$和$+10^{i+1}$谁更优,如果$10^{i+1}$更优就替换。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int s(ll x) {
	if(!x) return 0;
	return x%10+s(x/10);
}
int main() {
	int K;
	scanf("%d",&K);
	ll n=1,d=1;
	printf("%d\n",1); K--;
	while(K--) {
		if(s(n+10*d)*(n+d)>(n+10*d)*s(n+d)) d*=10;
		printf("%lld\n",n+=d);
	}
}

 

E - Independence

题意:给出一个无向图,让你将它分成两部分使得,每部分的点互相有边相连。

问最少有几条相连的边。

分析:设分成S,T两个集合,答案等于siz(S)*(siz(S)-1)/2+siz(T)*(siz(T)-1)/2。

这个式子的意思是让我们最小化siz(S)和siz(T)的差的绝对值。

考虑对补图进行操作。补图上有边相连的两个点一定分别属于两个不同的集合。

对每个连通块进行染色,同时求出来每个连通块的两个集合分别有多少点。

然后就是个DP了,F[i][j]表示考虑前i个连通块,siz(S)-siz(T)的差为j是否合法。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define GG puts("FUCK")
#define maxn 750
int map[750][750];
int n,m,a[750],b[750],tot;
int f[750][1550],vis[750];
void dfs(int x,int opt) {
	int i; vis[x]=opt;
	if(!opt) a[tot]++;
	else b[tot]++;
	for(i=1;i<=n;i++) {
		if(map[x][i]) {
			if(vis[i]==-1) {
				dfs(i,opt^1);
			}else if(vis[i]==opt) {
				puts("-1"); exit(0);
			}
		}
	}
}
int main() {
	scanf("%d%d",&n,&m);
	int i,j,x,y;
	memset(vis,-1,sizeof(vis));
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(i!=j) map[i][j]=1;
	for(i=1;i<=m;i++) {
		scanf("%d%d",&x,&y); map[x][y]=map[y][x]=0;
	}
	for(i=1;i<=n;i++) {
		if(vis[i]==-1) {
			tot++;
			dfs(i,0);
		}
	}
	f[0][maxn]=1;
	for(i=0;i<tot;i++) {
		int del=a[i+1]-b[i+1];
		for(j=0;j<=maxn+maxn;j++) {
			if(f[i][j]) {
				if(j>=del) f[i+1][j-del]=1;
				if(j+del<=maxn+maxn) f[i+1][j+del]=1;
			}
		}
	}
	int mn=1<<30;
	for(i=maxn;i<=maxn+maxn;i++) {
		if(f[tot][i]) {
			mn=i-maxn; break;
		}
	}
	for(i=maxn;i>=0;i--) {
		if(f[tot][i]) {
			mn=min(mn,maxn-i); break;
		}
	}
	int s=(n+mn)>>1,t=n-s;
	printf("%d\n",s*(s-1)/2+t*(t-1)/2);
}

 

F - Eating Symbols Hard

题意:给你一个字符串表示操作。

+表示在指针对应位置上的值+1

-表示在指针对应位置上的值-1

>表示把指针右移1位。

<表示把指针左移1位。

假设按原串操作后序列为A。

求有多少个子串使得操作后的序列B等于A。

分析:

观察到操作的$S$序列可以用哈希的方法变成一个多项式。
其中设$M$为一个大质数,$X$为一个$base$。对于字符串$S$,定义$t(S)$为其哈希值。
有$t(S)=(\sum\limits_{i}A_iX^{i})modM$
在S前加入一个字符后,有:
$t(0)=0$,为一个空串。
$t(+S)=t(S)+1$
$t(-S)=t(S)-1$
$t(>S)=t(S)X$
$t(<S)=t(S)X^{-1}$
令$C$为整个序列的哈希值,也就是我们要统计有多少$(i,j)$使得$i$到$j$的哈希值为$C$
处理出来前缀的系数,逆元的i次方和base的i次方。
然后map一下。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <map>
using namespace std;
typedef long long ll;
#define GG puts("FUCK")
#define N 500050
#define mr(x,y) make_pair(x,y)
inline char nc() {
	static char buf[100000],*p1,*p2;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
int rd() {
	int x=0; char s=nc();
	while(s<'0'||s>'9') s=nc();
	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
	return x;
}
char rc() {
	char s=nc();
	while(s!='+'&&s!='-'&&s!='>'&&s!='<') s=nc();
	return s;
}
int mod1=998244353,base1=19260817,inv1;
int mod2=353448299,base2=20000003,inv2;
int h1[N],mi1[N],imi1[N],p[N],n,C;
int h2[N],mi2[N],imi2[N];
map<pair<int,int>,int>mp;
int qp(int x,int y,int p) {
	int re=1;
	for(;y;y>>=1,x=ll(x)*x%p) if(y&1) re=ll(re)*x%p;
	return re;
}
int main() {
	inv1=qp(base1,mod1-2,mod1);
	inv2=qp(base2,mod2-2,mod2);
	n=rd(); char str;
	int i;
	for(mi1[0]=imi1[0]=mi2[0]=imi2[0]=i=1;i<=(n<<1);i++) {
		mi1[i]=ll(mi1[i-1])*base1%mod1;
		imi1[i]=ll(imi1[i-1])*inv1%mod1;
		mi2[i]=ll(mi2[i-1])*base2%mod2;
		imi2[i]=ll(imi2[i-1])*inv2%mod2;
	}
	p[0]=n;
	for(i=1;i<=n;i++) {
		str=rc();
		if(str=='+') {
			p[i]=p[i-1];
			h1[i]=(h1[i-1]+mi1[p[i]])%mod1;
			h2[i]=(h2[i-1]+mi2[p[i]])%mod2;
		}else if(str=='-') {
			p[i]=p[i-1];
			h1[i]=(h1[i-1]-mi1[p[i]]+mod1)%mod1;
			h2[i]=(h2[i-1]-mi2[p[i]]+mod2)%mod2;
		}else if(str=='>') {
			p[i]=p[i-1]+1;
			h1[i]=h1[i-1]; h2[i]=h2[i-1];
		}else {
			p[i]=p[i-1]-1;
			h1[i]=h1[i-1]; h2[i]=h2[i-1];
		}
		mp[mr(h1[i],h2[i])]++;
	}
	ll ans=0;
	for(i=1;i<=n;i++) {
		int d=p[i-1]-n,t1=h1[n],t2=h2[n];
		if(d>=0) t1=ll(t1)*mi1[d]%mod1,t2=ll(t2)*mi2[d]%mod2;
		else t1=ll(t1)*imi1[-d]%mod1,t2=ll(t2)*imi2[-d]%mod2;
		t1=(t1+h1[i-1])%mod1;
		t2=(t2+h2[i-1])%mod2;
		ans+=mp[mr(t1,t2)];
		mp[mr(h1[i],h2[i])]--;
	}
	printf("%lld\n",ans);
}

 

转载于:https://www.cnblogs.com/suika/p/9252914.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值