2020 CCPC Wannafly Winter Camp Day7 解题报告

K


每组通关独立考虑,取最值。对于一组组合来说题目可转化为求最小的n,所以二分答案即可。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=1007;

#define PII pair<ll,ll>
#define fr first
#define sc second
#define mp make_pair

PII a[N];
ll ans[N];

ll get(ll a,ll x){
	ll l=0,r=1e9,res=0;
	while(l<=r){
		ll mid=(l+r)/2;
		if((2*a+mid+1)*mid/2<x) l=mid+1;
		else res=mid,r=mid-1;
	}
	return res;
}

int main(){
	ll a1=input(),a2=input();
	int n=input();
	ll Ans=0x3f3f3f3f3f3f3f;
	for(int i=1;i<=n;i++){
		a[i].fr=input(),a[i].sc=input();
		ans[i]=get(a1,a[i].fr);
		ans[i]=max(ans[i],get(a2,a[i].sc));
		Ans=min(Ans,ans[i]);
	}
	printf("%lld\n",Ans);
}

H


每组数字被删除的情况是等可能的,只要统计有多少组数字胡志就行了。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

int a=0,b;

int main(){
	int n=input();
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			a+=__gcd(i,j)==1;
		}
	}
	// cout<<a<<endl;
	b=(n-1)/2*2+1;
	if(a==0) b=1;
	else{
		int tmp=__gcd(a,b);
		a/=tmp,b/=tmp;
	}
	printf("%d/%d\n",a,b);
}

G


对于\(k \le nm\)的情况爆搜就行。

对于\(k > nm\)的情况答案显然是随便找一条哈密尔顿回路走,此时的答案显然是:\(\sum a_{i,j}+\frac{((nm-1)nm)}{2}+(k-nm)nm\)


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

ll n,m,x,y,k;
ll a[15][15];
ll vis[15][15];

ll dfs(ll dep,ll tx,ll ty){
	if(dep>=k) return 0;
	if(tx<1||tx>n||ty<1||ty>m) return 0;
	ll res=0,t=vis[tx][ty];
	vis[tx][ty]=-(a[tx][ty]+dep);
	res=max(dfs(dep+1,tx+1,ty),res);
	res=max(dfs(dep+1,tx-1,ty),res);
	res=max(dfs(dep+1,tx,ty+1),res);
	res=max(dfs(dep+1,tx,ty-1),res);
	
	vis[tx][ty]=t;
	
	res+=a[tx][ty]+vis[tx][ty]+dep;

	return res;
}

int main(){
	n=input(),m=input();
	x=input(),y=input();
	k=input();

	ll sum=0,Ans=0;

	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			a[i][j]=input();
			vis[i][j]=0;
			sum+=a[i][j];
		}
	}

	if(k<n*m){
		Ans=dfs(0,x,y);
	}else{
		Ans=sum+((n*m-1)*n*m)/2+(k-(n*m))*(n*m);
	}
	printf("%lld\n",Ans);
}

L


首先我们注意到\(n<=20\),可知总状态数不会多余\(2^{20}\),似乎可以通过状压来做。我们可以简单建个模,所有状态都有只有唯一一个后继状态,我们不妨用有向边把前驱连向后继,似乎可以找到一点规律。我们感性的思考一下这个状态图会长成什么样子,如果所有的情况都列出来那么最多有一条长为\(2^{20}\)的链,但到\(2^{20}+1\)个状态时势必会回到前面的某个状态,那么有前面每个前驱只有唯一一个后继的性质可知图中必然会存在一个环。所以这个状态图是一环上连着一个链,是一棵基环内向树(好像被叫成基环外向树也是可以的)。那么求答案只要分类讨论和二分就行了。

关于这个题的另一个重点就是如何去求答案,首先我们不妨对原图上一个点的入点进行状压,然后再让他与上当前节点的颜色状态,最后只要统计他们的与的结果二进制位上有多少个\(1\)就知道在下一状态该点的颜色。我们可以通过这种方法把所有答案预处理出来。然后就是统计在某个状态上该点是黑还是白,算一个前缀和方便我们二分以便最后算答案。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const ll N=21,M=1<<N;

int G[N],vis[M],b[M],cnt=0;
int c0[M][N],c1[M][N];

int main(){
	int n=input(),m=input(),q=input();

	int sta=0;

	for(int i=0;i<n;i++){
		int x=input();
		sta|=x<<i;
	}

	for(int i=1;i<=m;i++){
		int u=input()-1,v=input()-1;
		G[v]|=1<<u;
	}

	memset(vis,-1,sizeof(vis));
	vis[sta]=0,b[0]=sta;
	int chain=0,loop=0;

	while(1){
		int now=0;
		for(int i=0;i<n;i++)
			now|=(__builtin_parity(G[i]&sta)<<i);

		b[++cnt]=sta=now;

		if(~vis[now]){
			chain=vis[now];
			loop=cnt-chain;
			break;
		}
		vis[now]=cnt;
	}

	for(int i=0;i<=chain;i++)
		for(int j=0;j<n;j++)
			c0[i][j]=(b[i]>>j&1);

	for(int i=1;i<=chain;i++)
		for(int j=0;j<n;j++)
			c0[i][j]+=c0[i-1][j];

	for(int i=1;i<=loop;i++)
		for(int j=0;j<n;j++)
			c1[i][j]=c1[i-1][j]+(b[i+chain]>>j&1);

	for(int i=1;i<=q;i++){
		int x=input()-1,k=input();
		if(k<=c0[chain][x]){
			int l=0,r=chain;
			while(l<r){
				int mid=(l+r)>>1;
				if(c0[mid][x]<k) l=mid+1;
				else r=mid;
			}
			printf("%d\n",l);
		}else{
			k-=c0[chain][x];
			ll ans=chain;
			if(!c1[loop][x])printf("-1\n");
			else{
				if(k>=c1[loop][x]){
					if(k%c1[loop][x]){
						ans+=k/c1[loop][x]*loop;
						k%=c1[loop][x];
					}else{
						ans+=(k/c1[loop][x]-1)*loop;
						k=c1[loop][x];
					}
				}

				int l=1,r=loop;

				while(l<r){
					int mid=(l+r)>>1;
					if(c1[mid][x]<k) l=mid+1;
					else r=mid;
				}
				printf("%d\n",ans+l);
			}
		}
	}
}

A


对于一对元素\((i,j)\),一定满足\(min(s_i,s_j)<k<max(s_i,s_j)\),每个元素对对答案的贡献是\(2^{n-(j-i+1)}\),拆开后发现\(i\)\(j\)可以分开来算。我们可以考虑答案\(k\)和答案\(k+1\)的关系,\(k\)的答案到\(k+1\)的答案显然是减掉了\((x,k+1),x\in[1,k-1]\)范围的贡献,增加了\((k,x),x\in[k,n]\)的贡献。于是我们不妨求出所有的\(\delta_k=Ans_{k+1}-Ans_k\)。便可求得答案了。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

#define lowbit(x) x&-x

const int N=1e5+7;
const int mod=1e9+7;

int n;
int t[N],a[N];
int inv[N],delta[N];

ll add(ll a,ll b){return (a+b)%mod;}
ll sub(ll a,ll b){return (a-b+mod)%mod;}
void update(int pos,ll x){while(pos<=n) t[pos]=add(t[pos],x),pos+=lowbit(pos);}
ll query(int x){ll res=0;while(x) res=add(res,t[x]),x-=lowbit(x);return res;}

int main(){
	n=input();

	inv[0]=1;
	for(int i=1;i<=n;i++){
		a[i]=input();
		inv[i]=(inv[i-1]*2)%mod;
	}

	for(int i=1;i<=n;i++){
		ll tmp=query(a[i]-1)*inv[n-i]%mod;
		delta[a[i]]=sub(delta[a[i]],tmp);
		tmp=sub(query(n),query(a[i]))*inv[n-i]%mod;
		delta[a[i]+1]=add(delta[a[i]+1],tmp);
		update(a[i],inv[i-1]);
	}

	memset(t,0,sizeof(t));

	for(int i=n;i>=1;i--){
		ll tmp=sub(query(n),query(a[i]))*inv[i-1]%mod;
		delta[a[i]+1]=add(delta[a[i]+1],tmp);
		tmp=query(a[i]-1)*inv[i-1]%mod;
		delta[a[i]]=sub(delta[a[i]],tmp);
		update(a[i],inv[n-i]);
	}

	for(int i=1;i<=n;i++){
		delta[i]=add(delta[i-1],delta[i]);
		printf("%lld\n",delta[i]);
	}
}
posted @ 2020-03-04 17:46  _aether  阅读(206)  评论(0编辑  收藏  举报