AtCoder Grand Contest 034

一直很想找几套数字有特殊意义的考试题做做,终于找到时间了。

其实个人认为\(Atcoder\)上的题目比较思维化,有点意思……(主要是比\(Codeforces\)好看)

Atcoder Grand Contest 034 entry

\(\text{A - Kenken Race}\)

考虑两种情况:

\(1.\)\(a<b\quad c<d\),则只要分别可以到达即可。

\(2.\)否则,只需在\(b-1\)\(d+1\)的路上有长度大于等于\(3\)的一串空格即可。

代码如下,实现简单,思路简单:

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,a,b,c,d,p[maxn];
int f1[maxn],f2[maxn];
int main(){
	n=read();
	a=read(),b=read();
	c=read(),d=read();
	for(int i=1;i<=n;i++)
		p[i]=getchar()=='.';
	f1[a]=f2[b]=1;
	for(int i=a+1;i<=c;i++)
		f1[i]=(p[i]&(f1[i-1]|f1[i-2]));
	for(int i=b+1;i<=d;i++)
		f2[i]=(p[i]&(f2[i-1]|f2[i-2]));
	if(!f1[c]||!f2[d]||c==d)return puts("No"),0;
	if(c<d)return puts("Yes"),0;
	int cnt=0;
	for(int i=b-1;i<=d+1;i++)
		if(!p[i]){if(cnt>=3)return puts("Yes"),0;cnt=0;}
		else cnt++;
	if(cnt>=3)puts("Yes");
	else puts("No");
	return 0;
}

正好\(34\)行呵(真不戳)。

\(\text{B - ABC}\)

个人觉得样例\(1\)的提示性有点强。

结论就是一个\(ABC\)的贡献是其前导\(A\)的个数,操作过程样例\(1\)解释都给了。

那么简单维护一下前导\(A\)统计答案即可,记得开\(long long\)

代码如下,仅供参考:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
int n,cnt,pos,tot;
char s[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
signed main(){
	scanf("%s",s+1);
	n=strlen(s+1),pos=1;
	while(pos<=n){
		//printf("%d %d %d\n",pos,cnt,tot);
		if(s[pos]=='A'&&s[pos+1]=='B'&&s[pos+2]=='C'){
			s[pos+2]='A';cnt+=1+tot;pos+=2;
		}else if(s[pos]=='A')tot++,pos++;
		else tot=0,pos++;
	}
	printf("%lld\n",cnt);
	return 0;
}

\(\text{C - Tests}\)

二分答案,利用一次函数的单调性,枚举。

时间复杂度\(\Theta(nlogn)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
int n,k,sum;
struct node{int l,r,b,f;}p[maxn];
inline int cmp(node x,node y){return x.f>y.f;}
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int pre[maxn];
inline int calc(int id,int x){
	if(x<=p[id].b)return x*p[id].l;
	return p[id].b*p[id].l+p[id].r*(x-p[id].b);
}
inline bool check(int val){
	int t=val/k,q=val%k;
	int res=0;
	for(int i=1;i<=n;i++)
		if(i<=t)res=max(res,pre[t+1]-p[i].f+calc(i,q));
		else res=max(res,pre[t]+calc(i,q));
	return res>=sum;
}
signed main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++){
		p[i].b=read();
		p[i].l=read(),p[i].r=read();
		sum+=p[i].b*p[i].l;
		p[i].f=calc(i,k);
	}
	sort(p+1,p+1+n,cmp);
	int l=0,r=0;
	for(int i=1;i<=n;i++)
		r+=p[i].b;
	for(int i=1;i<=n;i++)
		pre[i]=pre[i-1]+p[i].f;
	int ans=r;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%lld\n",ans);
	return 0;
}

\(\text{D - Manhattan Max Matching}\)

显然使用费用流,但朴素建边会\(T\)飞,考虑建虚点优化建边。

显然如果要将\(a(x_a,y_a)\)上的点与\(b(x_b,y_b)\)上的点配对的话,其代价为\(dis_{a,b}=|a_x-b_x|+|a_y-b_y|\)

这个式子有四种可能,如下:

\(a_x-b_x+a_y-b_y\quad\)\(a_x+a_y\)\(-b_x-b_y\)

\(a_x-b_x+b_y-a_y\quad\)\(a_x-a_y\)\(-b_x+b_y\)

\(b_x-a_x+a_y-b_y\quad\)\(-a_x+a_y\)\(b_x-b_y\)

\(b_x-a_x+b_y-a_y\quad\)\(-a_x-a_y\)\(b_x+b_y\)

发现\(dis\)即这四个值中的最大值,也是最小值的相反数。

故我们建四个虚点,分别连每个点的\(x+y\)\(x-y\)\(-x+y\)\(-x-y\)

跑最小费用最大流时一定会走两点欧几里得距离的相反数。

故跑一遍费用流,输出最小费用的相反数即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x7f
const int maxn=1e5+100;
int beg[maxn],nex[maxn],to[maxn],f[maxn],c[maxn],e,mf,mc;
void add(int x,int y,int z,int d){
	nex[e]=beg[x];beg[x]=e;
	to[e]=y;f[e]=z;c[e]=d;e++;
}
int n,m,st,ed,dis[maxn],vis[maxn],pre[maxn],val[maxn],flow[maxn];
queue<int>q;
int spfa(){
	memset(dis,inf,sizeof(dis));
	memset(flow,inf,sizeof(flow));
	memset(vis,0,sizeof(vis));
	dis[st]=0;pre[ed]=-1;vis[st]=1;
	q.push(st);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		vis[x]=0;
		for(int i=beg[x];~i;i=nex[i]){
			int t=to[i];
			if(f[i]&&dis[t]>dis[x]+c[i]){
				dis[t]=dis[x]+c[i];
				pre[t]=x;val[t]=i;
				flow[t]=min(flow[x],f[i]);
				if(!vis[t]){
					vis[t]=1;
					q.push(t);
				}
			}
		}
	}
	return pre[ed]!=-1;
}
signed main(){
	cin>>n;
	int x,y,z,p1,p2,p3,p4;
	memset(beg,-1,sizeof(beg));
	st=n+n+1;ed=st+1;
	p1=ed+1,p2=p1+1,p3=p2+1,p4=p3+1;
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&x,&y,&z);
		add(st,i,z,0);add(i,st,0,0);
		add(i,p1,inf,x+y);add(p1,i,0,-x-y);
		add(i,p2,inf,x-y);add(p2,i,0,y-x);
		add(i,p3,inf,y-x);add(p3,i,0,x-y);
		add(i,p4,inf,-x-y);add(p4,i,0,x+y);
	}
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&x,&y,&z);
		add(i+n,ed,z,0);add(ed,i+n,0,0);
		add(p1,i+n,inf,-x-y);add(i+n,p1,0,x+y);
		add(p2,i+n,inf,y-x);add(i+n,p2,0,x-y);
		add(p3,i+n,inf,x-y);add(i+n,p3,0,y-x);
		add(p4,i+n,inf,x+y);add(i+n,p4,0,-x-y);
	}
	while(spfa()){
		int now=ed;
        mf+=flow[ed];
        mc+=flow[ed]*dis[ed];
        while(now!=st){
            f[val[now]]-=flow[ed];
            f[val[now]^1]+=flow[ed];
            now=pre[now];
        }
	}
	printf("%lld\n",-mc);
	return 0;
}

\(\text{E - Complete Compress}\)

神仙题,居然在冬令营上被讲到了。

考虑建成一个集合互相抵消的模。

我们要将这些集合最终抵消完,考虑最大的集合。

若这个集合的数量小于等于半数,则可以消集合大小的半数次,即\([\frac{size}{2}]\)

若这个集合的数量大于半数,则用外部的消完后本集合内再相互抵消。

每个点做一遍树形\(dp\),复杂度\(\Theta(n^2)\)

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e3+10;
const int mod=1e9+7;
int n,a[maxn],cnt;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
string s;
int beg[maxn],nex[maxn<<1],to[maxn<<1],e;
inline void add(int x,int y){
	e++;nex[e]=beg[x];
	beg[x]=e;to[e]=y;
}
int val[maxn];
int dis[maxn],f[maxn],ans=inf;
inline void solve(int x,int fa){
	val[x]=a[x];dis[x]=0;
	int son=0;
	for(int i=beg[x];i;i=nex[i]){
		int t=to[i];
		if(t==fa)continue;
		solve(t,x);
		val[x]+=val[t];
		dis[x]+=(dis[t]+=val[t]);
		if(dis[t]>dis[son])son=t;
	}
	if(!son)return void(f[x]=0);
	if(dis[x]>=2*dis[son])f[x]=dis[x]/2;
	else f[x]=dis[x]-dis[son]+min(f[son],(dis[son]*2-dis[x])/2);
}
int main(){
	n=read();cin>>s;
	for(int i=1;i<=n;i++)
		if(s[i-1]=='1')a[i]=1,cnt++;
	int x,y;
	for(int i=1;i<n;i++){
		x=read(),y=read();
		add(x,y),add(y,x);
	}
	for(int i=1;i<=n;i++){
		solve(i,0);
		if(!(dis[i]&1)&&f[i]==dis[i]/2)
			ans=min(ans,dis[i]/2);
	}
	printf("%d\n",ans==inf?-1:ans);
	return 0;
}

\(\text{F - RNG and XOR}\)

神仙\(fwt\),基本不可做,操作仙得一\(mp\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=(1<<18)+10;
const int mod=998244353;
const int inv2=(mod+1)/2;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=res*x%mod;
		x=x*x%mod;y>>=1;
	}
	return res;
}
int n,sum,len,p[maxn],q[maxn],f[maxn];
inline void fwt(int *a,int n,int flag){
	for(int i=2;i<=n;i<<=1)
		for(int j=0,p=i>>1;j<n;j+=i)
			for(int k=j;k<j+p;k++){
				int x=a[k],y=a[k+p];
				a[k]=(x+y)%mod;
				a[k+p]=(x+mod-y)%mod;
				if(!flag){
					a[k]=a[k]*inv2%mod;
					a[k+p]=a[k+p]*inv2%mod;
				}
			}
}
signed main(){
	n=read();len=1<<n;
	for(int i=0;i<len;i++){
		p[i]=read();
		sum=(sum+p[i])%mod;
	}
	sum=ksm(sum,mod-2);
	for(int i=0;i<len;i++)
		p[i]=p[i]*sum%mod;
	q[0]=len-1;p[0]+=mod-1;
	for(int i=1;i<len;i++)
		q[i]=mod-1;
	fwt(p,len,1);fwt(q,len,1);
	for(int i=0;i<len;i++)
		f[i]=q[i]*ksm(p[i],mod-2)%mod;
	fwt(f,len,0);
	int tmp=mod-f[0];
	for(int i=0;i<len;i++)
		printf("%lld\n",(f[i]+tmp)%mod);
	return 0;
}
posted @ 2020-12-21 13:37  syzf2222  阅读(102)  评论(0编辑  收藏  举报