• 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("
    ");
    	for (i=0;i<=k;i++) 
    	for (j=head[i];j!=-1;j=edge[j].next) 
            
            printf("%d %d %d
    ",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
    ",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
    ",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
    ",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
    ",v[prime[i]][1],-v[prime[i]][0]);
    			}
    		}
    		int ans=sap(0,n+1,n+2);
    		printf("%d
    ",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
    ",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
    ",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
    ",ans);
    	}
    }
    
  • 相关阅读:
    sublime打开txt文件乱码的问题
    while循环小例
    mongoDB内置文档定义
    WebStorm 10.0.3注册码
    angularjs之ng-mode获取lobject类型里的键值
    前端打印console
    js去掉数组的空字符串
    js数组去重的三种方式的比较
    js数据类型之判断
    Bootstrap中的datetimepicker浅谈
  • 原文地址:https://www.cnblogs.com/nowheretrix/p/9386001.html
Copyright © 2020-2023  润新知