1. 程式人生 > >ACM International Collegiate Programming Contest, JUST Collegiate Programming Contest (2018)

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

debug -a struct 人性 pro ati 銀河 ans n)

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);
	}
}

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