ACM International Collegiate Programming Contest, JUST Collegiate Programming Contest (2018)

ACM International Collegiate Programming Contest, JUST Collegiate Programming Contest (2018)


B. New Assignment

  • 有n个人(1 ≤ n ≤ 104),有男有女,每个人都有一个id,现在这n个人分成学习互助小组,有三种组队模式,一个男人一组,一个女人一组,一男一女一组,如果要一男一女一组,那么这两人id的gcd要>1。保证任意三个人的gcd=1。求小组的组数最少是多少?
  • 看起来是一个很裸的二分匹配,当两个人性别为男女,并且gcd>1时,连边。可是这里的连边的时候复杂度很高。直接暴力连边为5000 × 5000×log级别的。所以,需要换一个角度考虑。
  • 可以发现,由于任意三个数的gcd=1的性质,可以保证任何一个质因子最多有两个被除数。当且仅当这两个被除数的性别不同连边。
#include"stdio.h"
#include"string.h"
#include"queue"
#include"vector"
#include"algorithm"
#include"iostream"
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=100010;//点数 
const int maxm=400010;//边数
struct node{
	int v,next,cap,flow;
}edge[maxm];
int cnt;
int head[maxn];
int cur[maxn],d[maxn];// 当前弧下标   结点到汇点弧长 
int p[maxn],gap[maxn];////可增广路上的上一条弧   gap优化
int ss[maxn];//保存路径
void init(){
	cnt=-1;
	memset(head,-1,sizeof(head));
} 
void debug(int k){
	int i,j;
	printf("\n");
	for (i=0;i<=k;i++) 
	for (j=head[i];j!=-1;j=edge[j].next) 
        
        printf("%d %d %d\n",i,edge[j].v,edge[j].flow);
}
void add(int u,int v,int w,int rw=0){
	cnt++;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	edge[cnt].cap=w;
	edge[cnt].flow=0;
	head[u]=cnt;
	cnt++;
	edge[cnt].v=u;
	edge[cnt].next=head[v];
	edge[cnt].cap=rw;
	edge[cnt].flow=0;
	head[v]=cnt;
}
void bfs(int k){
	int v,i;
	int u;
	memset(gap,0,sizeof(gap));
	memset(d,-1,sizeof(d));
	d[k]=0;
	gap[0]++;
	queue<int> q;
	q.push(k);
	while(q.empty()==false){
		u=q.front();q.pop();
		for (i=head[u];i!=-1;i=edge[i].next){
			v=edge[i].v;
			if (d[v]==-1) {
				d[v]=d[u]+1;
				gap[d[v]]++;
				q.push(v);
			}
		}
	}
}
int sap(int s,int t,int N){
	int i;
	bfs(t);
	memcpy(cur,head,sizeof(head));
	int top=0;
	int u=s;
	int ans=0;
	while(d[s]<N){
		if (u==t){
			int min=inf;
			int inser;
			for (i=0;i<top;i++){
				if (min>=edge[ss[i]].cap-edge[ss[i]].flow){
					min=edge[ss[i]].cap-edge[ss[i]].flow;
					inser=i;//这跟管子是满流了 
				}
			}
			for (i=0;i<top;i++){
				edge[ss[i]].flow+=min;
				edge[ss[i]^1].flow-=min;
			} 
			ans+=min;
			top=inser;
			u=edge[ss[top]^1].v;//u为满流的那个结点 也就是那根管子的倒管子的终点
			continue;
		}
		bool ok=false;
		int v;
		for (i=cur[u];i!=-1;i=edge[i].next){ 
			v=edge[i].v;
			if (edge[i].cap-edge[i].flow>0&&d[v]+1==d[u]){
				ok=true;
				cur[u]=i;
				break;
			}//u后 添加了一条下标为i的弧 
		}
		if(ok==true){
			ss[top]=cur[u];
			top++;
			u=v;
			continue;
		}
		//如果这条路已经走不通了,那么撤退
		int min=N;
		for (i=head[u];i!=-1;i=edge[i].next)
		  if (edge[i].cap-edge[i].flow>0&&d[edge[i].v]<min){
		  	min=d[edge[i].v];
		  	cur[u]=i;
		  }
		gap[d[u]]--;
		if (gap[d[u]]==0) return ans;
		d[u]=min+1;
		gap[d[u]]++;
		if (u!=s) {
			top--;
			u=edge[ss[top]^1].v;
			
	}
	}
	return ans;
}
int gcd(int a,int b){
	if (a%b==0) return b;
	return gcd(b,a%b);
}
int prime[600000]={0},numprime=0;
bool isNotPrime[1000010]={1,1};
void sushu(int N){
  long long int i;
   for (i=2;i<=N;i++) {
   	     if(! isNotPrime[i])                 
            prime[numprime ++]=i;    
        
        for(long j = 0 ; j < numprime && i * prime[j] <  N ; j ++)  
            {                 
                isNotPrime[i * prime[j]] = 1;    
            if( !(i % prime[j] ) )                   
                break;             
        }          
    }          
}
int a[10500];
char s[10];
vector<int > v[1000005];
int vis[1000005];
int main(){
	int e,t,i,j,x,y,cap,now;
	int n,m;
	sushu(1000000);
	scanf("%d",&t);
	for (e=1;e<=t;e++){
		memset(vis,0,sizeof(vis));
		for (i=0;i<numprime;i++) v[prime[i]].clear();
		init();
		scanf("%d",&n);
		for (i=1;i<=n;i++) scanf("%d",&a[i]);
		for (j=1;j<=n;j++) {
			scanf("%s",s);
		
			if (s[0]=='M') {
				now=a[j];
				for (i=0;i<numprime&&prime[i]<=1000;i++) {
				//	printf("%d\n",now);
					if (now%prime[i]==0) v[prime[i]].push_back(j);
					while (now%prime[i]==0) now=now/prime[i];
					if (now==1||isNotPrime[now]==0) break;	
				}	
				if (isNotPrime[now]==0) v[now].push_back(j);
			} else {
				now=a[j];
				for (i=0;i<numprime&&prime[i]<=1000;i++) {
					if (now%prime[i]==0) v[prime[i]].push_back(-j);
					while (now%prime[i]==0) now=now/prime[i];
					if (now==1||isNotPrime[now]==0) break;	
				}
				if (isNotPrime[now]==0) v[now].push_back(-j);
			}
		}
		//	printf("%d %d %d\n",prime[0],v[prime[0]][0],v[prime[0]][1]);
		for (i=0;i<numprime;i++) 
		if (v[prime[i]].size()==2&&v[prime[i]][0]*v[prime[i]][1]<0) {
			
			if (v[prime[i]][0]>0) {
				
				if (vis[v[prime[i]][0]]==0) {add(0,v[prime[i]][0],1);vis[v[prime[i]][0]]=1;}
				if (vis[-v[prime[i]][1]]==0) {add(-v[prime[i]][1],n+1,1);vis[-v[prime[i]][1]]=1;}
				add(v[prime[i]][0],-v[prime[i]][1],inf);
			//	printf("%d %d\n",v[prime[i]][0],-v[prime[i]][1]);
			}
			else {
				if (vis[v[prime[i]][1]]==0) {add(0,v[prime[i]][1],1);vis[v[prime[i]][1]]=1;}
				if (vis[-v[prime[i]][0]]==0) {add(-v[prime[i]][0],n+1,1);vis[-v[prime[i]][0]]=1;}
				add(v[prime[i]][1],-v[prime[i]][0],inf);
			//	printf("%d %d\n",v[prime[i]][1],-v[prime[i]][0]);
			}
		}
		int ans=sap(0,n+1,n+2);
		printf("%d\n",n-ans);
	}
}

E. Maximum Sum

  • 取数问题。16*16的矩阵,如果你取了这个数,那么周围8个格子的数都不能取。求取的数和最大。
  • dp瞎搞。事先筛选出行内任意两个相邻位置不同的状态,大约有3000种不到。
  • dp[i][j]代表第i行,这一行的取数方案为j时,前i行的最大和。由于第i-1行无法去影响i+1行,故可以这样设置状态。
  • 考虑转移, 设上一行的状态为k,当前行的状态为j,如果j and k==0 并且 j<<1 and k ==0 并且 j and k<<1 ==0 那么表示可以转移。
  • 尽管算下来是超高的复杂度,不过千万不要低估银河评测机的实力。
#include"stdio.h"
#include"string.h"
#include"vector"
#include"algorithm"
using namespace std;
vector<int> v;
int d[20];
int a[50][50];
int dp[17][2600];
int main(){
	int i,j,k,l;
	int e,t,n;
	int ans,x;
	int tmp;
	d[0]=1;
	v.clear();
	for (i=1;i<=17;i++) d[i]=d[i-1]*2;
	for (i=0;i<=d[16]-1;i++) {
		int sign=0;
		for (j=0;j<=14;j++) 
			if ((i&d[j])>0&&(i&d[j+1])>0) {sign=1;break;}
		if (sign==0) v.push_back(i);
	}
	
	l=v.size();
	scanf("%d",&t);
	for (e=1;e<=t;e++) {
		scanf("%d",&n);
		for (i=1;i<=n;i++)
		for (j=1;j<=n;j++) scanf("%d",&a[i][j]);
		memset(dp,0,sizeof(dp));
		for (i=0;i<l&&v[i]<=d[n]-1;i++) {
			for (j=1;j<=n;j++) if ((v[i]&d[j-1])>0) dp[1][i]+=a[1][j];
		//	printf("%d :%d\n",i,dp[1][v[i]]);
		}
		for (k=2;k<=n;k++) {
			for (i=0;i<l&&v[i]<=d[n]-1;i++) {
			int tmp=0;
			for (x=1;x<=n;x++) if ((v[i]&d[x-1])>0) tmp+=a[k][x];
			for (j=0;j<l&&v[j]<=d[n]-1;j++) 
			if ((v[i]&v[j])==0&&((v[i]<<1)&v[j])==0&&(v[i]&(v[j]<<1))==0) {

		    	dp[k][i]=max(dp[k][i],tmp+dp[k-1][j]);
		//    	printf("%d :%d :%d\n",k,v[i],dp[k][v[i]]);
		    }
		}
		}
		ans=0;
		for (i=0;i<l&&v[i]<=d[n]-1;i++) ans=max(dp[n][i],ans);
		printf("%d\n",ans);
	}
}

猜你喜欢

转载自www.cnblogs.com/nowheretrix/p/9386001.html