ACM International Collegiate Programming Contest, JUST Collegiate Programming Contest (2018)
阿新 • • 發佈:2018-07-29
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)