2019ICPC南昌題解

B-A Funny Bipartite Graph

題意:給出每一邊的點數都不超過n18n\leq18的二分圖,對左邊的點標號ii11nn,對右邊的點標號jj11nn,數據保證不會出現iijj的連邊(i>j)(i>j),保證左邊的每個點的度數都在1133之間。並且給出一個矩陣AA,在這個矩陣中,如果Ai,j=1A_{i,j}=1,那麼左邊的點ii和右邊的點jj不可以同時出現,並且要求右邊所有的點都有連邊。求所有方案中i=1nwidegi\sum_{i=1}^{n}w_{i}^{deg_{i}}的最小值。
做法:特判掉右邊存在某個點無邊可連的情形,直接輸出1-1。否則由於wiw_{i}爲正,對於右邊的點沒有必要連超過11條邊,因此搜索的上界不超過2183162*18*3^{16},且有不少的剪枝空間,可以通過。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
char str[20];
int g[20][20],A[20][20];
int w[20],tag[20],deg[20]; 
vector<int> go[20],con[20];
int ans=2e9;  
int cal(int x,int y) {
	if(!y) return 0; 
	int ans=1;
	for(int i=1;i<=y;++i) 
		ans=(ans*x);
	return ans; 
}
int n;  
void dfs(int cur,int A) {
	if(cur==n+1) { ans=min(ans,A);return; } 
	if(A>=ans) return;
	for(auto &v:go[cur]) {
		if(!tag[v]) {
			for(auto &x:con[v]) tag[x]++;
			deg[v]++;
			dfs(cur+1,A+cal(w[v],deg[v])-cal(w[v],deg[v]-1));
			deg[v]--;
			for(auto &x:con[v]) tag[x]--;
  		}
	}
}
void init() {
	memset(tag,0,sizeof(tag));
	memset(deg,0,sizeof(deg)); 
	for(int i=1;i<=n;i++) {
		go[i].clear();
		con[i].clear(); 
	}
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		init(); 
		scanf("%d",&n);
		for(int i=1;i<=n;i++) {
			scanf("%s",str+1);
			for(int j=1;j<=n;j++)  {
				g[i][j]=str[j]-'0';
				if(g[i][j]) go[j].push_back(i); 	
			}	
		}
		for(int i=1;i<=n;i++) {
			scanf("%s",str+1);
			for(int j=1;j<=n;j++) {
				A[i][j]=str[j]-'0'; 
				if(A[i][j]) {
					con[i].push_back(j);
					con[j].push_back(i); 
				}
			}	
		}
		for(int i=1;i<=n;i++)
			scanf("%d",&w[i]); 
		bool ok=1; 
		for(int i=1;i<=n;i++) {
			if(!go[i].size()) {
				ok=0;
				break;
			}
		}
		if(!ok) puts("-1");
		else {
			ans=2e9; 
			dfs(1,0); 
			if(ans==2e9) puts("-1");
			else printf("%d\n",ans); 	
		}
	} 
	return 0;
}

C-And and Pair

題意:給出一個長度不超過1e51e5的二進制01串,然後求問有多少個點對(i,j)(i,j)滿足0jin0 \leq j \leq i \leq n並且i&n=ii\&n=i並且i&j=0i\&j=0.
做法:顯然(0,0)(0,0)是一個答案,否則就可以枚舉ii的二進制的最高位的位置,統計除去最高位的低位11的個數xx以及00的個數yy
對於00的位置,ii當前位一定爲00jj當前一定有兩種選擇,所以爲2y2^{y}
對於11的位置,枚舉ii11的位置個數,如果ii11顯然jj只能填00,而ii00jj有兩種選擇,所以爲i=0xCxi2xi=3x\sum_{i=0}^{x}C_{x}^{i}2^{x-i}=3^{x}
每個最高位對答案的貢獻是3x2y3^{x}*2^{y}

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int mod=1e9+7;
const int N=1e5+7;
char s[N];
int fpow(ll x,int y) {
    ll ans=1;
    while(y) {
        if(y&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        ll ans=0;
        scanf("%s",s+1);
        int n=strlen(s+1);
        int _1=0,_0=0;
        for(int i=n;i>=1;i--) {
            if(s[i]=='1') {
                ans+=1ll*fpow(3,_1)*fpow(2,_0)%mod;
                ans%=mod;
            }
            if(s[i]=='1') _1++;
            else _0++;
        }
        printf("%lld\n",(ans+1)%mod);
    }
    return 0;
}

E-Bob’s Problem

題意:給出一張V5e4V\leq5e4E5e5E\leq5e5的帶權圖,邊分爲黑邊和白邊,然後要求你選出一個邊集,邊集裏面的白邊的條數不超過kk,使得這個圖是完全連通的,如果無法連通輸出1-1,否則求出最大的權值和。
做法:按照先加黑邊再從權值從大往小加白邊的順序向圖中加邊。維護一個最大生成樹,黑邊的權值全部加入到答案,白邊最多加kk條。然後判斷是否已經連通,否則在判斷白邊是否加滿kk條,若爲加滿則把剩餘白邊從大到小補滿到圖中。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=1e6+5;
struct Edge{
    int u,v,w,c;
    bool operator <(const Edge &rhs) const {
        if(c<rhs.c) return 1;
        else if(c==rhs.c&&w>rhs.w) return 1;
        else return 0;
    }
}e[M];
int fa[M];
int fnd(int x) {
    if(fa[x]==x) return x;
    else return fa[x]=fnd(fa[x]);
}
bool cmp(int a,int b) {
    return a>b;
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++) {
            scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].c);
        }
        sort(e+1,e+1+m);
        ll ans=0;
        int cnt=0;
        vector<int> rest;
        rest.clear();
        for(int i=1;i<=m;i++) {
            if(e[i].c==0) {
                int u=e[i].u;
                int v=e[i].v;
                int U=fnd(u);
                int V=fnd(v);
                if(U==V) ans+=e[i].w;
                else fa[U]=V,ans+=e[i].w;
            }
            else if(e[i].c==1) {
                int u=e[i].u;
                int v=e[i].v;
                int U=fnd(u);
                int V=fnd(v);
                if(U==V) rest.push_back(e[i].w);
                else {
                    fa[U]=V;
                    ans+=e[i].w;
                    cnt++;
                    if(cnt==k) break;
                }
            }
        }
        set<int> s;
        s.clear();
        for(int i=1;i<=n;i++)
            s.insert(fnd(i));
        if(s.size()==1) {
            sort(rest.begin(),rest.end(),cmp);
            for(int i=0;i<min(k-cnt,(int)rest.size());i++) ans+=rest[i];
            printf("%lld\n",ans);
        }
        else {
            printf("%d\n",-1);
        }
    }
    return 0;
}

G-Eating Plan

題意:給出一個n1e5n\leq1e5的序列,序列的值是全排列的階乘模上998857459,然後給出若干給查詢,每次查詢給出一個數tt,求最小的區間長度xx,使得某個區間[L,R][L,R]滿足RL+1=xR-L+1=x並且i=1naix\sum_{i=1}^{n}a_{i}\geq x
做法:注意到9988574592802的倍數,因此序列中實際有效的值只有2802個,通過2802*2802的預處理對所有的區間暴力按照區間和排序,同時維護區間長度後綴最小值,這樣就可以做到二分做到單次loglog的查詢。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=998857459; 
struct Query {
	int val,id;
}a[3000]; 
int tot=0;
int fac[3000]; 
struct Two {
	int val,len;
	bool operator <(const Two &rhs) const {
		return val<rhs.val; 
	}
}b[2804*2804/2]; 
int mi[2804*2804/2];  
int p=0;
int main() {
	fac[0]=1;
	for(int i=1;i<=2802;i++)
		fac[i]=(1ll*fac[i-1]*i)%mod;
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) {
		int x;
		scanf("%d",&x);
		if(x<=2802) {
			a[++tot].val=fac[x];
			a[tot].id=i;	
		}
	}
	for(int i=1;i<=tot;i++) {
		int sum=0;
		for(int j=i;j<=tot;j++) {
			sum=(sum+a[j].val)%mod;
			int len=a[j].id-a[i].id+1;
			b[++p].val=sum;
			b[p].len=len;
		}
	}
	sort(b+1,b+1+p);
	for(int i=p;i>=1;i--) {
		if(i==p) mi[i]=b[i].len;
		else mi[i]=min(b[i].len,mi[i+1]);
	}
	while(m--) {
		int x;
		scanf("%d",&x);
		int id=lower_bound(b+1,b+1+p,Two{x,0})-b;
		if(id==p+1) puts("-1");
		else printf("%d\n",mi[id]); 
	}
	return 0;
}

K-Tree

題意:給出一個n1e5n\leq1e5的樹,求問這個樹上有多少個點對(x,y)(x,y)滿足:
1.1. 兩個點x,yx,y互相不會成爲彼此的祖先。
2.2. 兩個點的權值wx+wy=2wlca(x,y)w_{x}+w_{y}=2*w_{lca(x,y)}lca(x,y)lca(x,y)xxyy的最近公共祖先。
3.3. 兩個點的距離不會超過kk
做法:考慮啓發式合併,先對樹重鏈剖分,然後對於每個子樹ii的答案,把ii作爲lcalca統計答案,先計算輕邊但不保留信息,再計算重邊且保留信息,最後再去計算輕邊統計答案。用平衡樹維護每個權值對於所有點的深度,暴力統計。

#include<bits/stdc++.h>
#include<bits/extc++.h>
typedef long long ll;
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
const int N=2e5+7;
int n,k;
ll ans=0;
vector<int> go[N];
int w[N];
int sz[N],d[N],son[N];
tree<pii, null_type, less<pii>, rb_tree_tag, tree_order_statistics_node_update> t[N];
void dfs(int u) {
    sz[u]=1;
    for(auto &v:go[u]) {
        d[v]=d[u]+1;
        dfs(v);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])
            son[u]=v;
    }
}
void merge(int u,int lca) {
    int x=2*w[lca]-w[u];
    if(x>=0) ans+=t[x].order_of_key(pii(2*d[lca]-d[u]+k,2e9));
    for(auto &v:go[u]) merge(v,lca);
}
void update(int u,int lca,int opt) {
    if(opt==1) t[w[u]].insert(pii(d[u],u));
    else if(opt==-1) t[w[u]].erase(pii(d[u],u));
    for(auto &v:go[u]) update(v,lca,opt);
}
void dsu(int u,bool ok) {
    for(auto &v:go[u]) {
        if(v==son[u]) continue;
        dsu(v,0);
    }
    if(son[u]) dsu(son[u],1);
    for(auto &v:go[u]) {
        if(v==son[u]) continue;
        merge(v,u);
        update(v,u,1);
    }
    t[w[u]].insert(pii(d[u],u));
    if(!ok) update(u,u,-1);
}
int main() {
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]);
    for(int i=2;i<=n;i++) {
        int fa;
        scanf("%d",&fa);
        go[fa].push_back(i);
    }
    dfs(1);
    dsu(1,1);
    printf("%lld\n",2*ans);
    return 0;
}

L-Who is the Champion

題意:簽到題,給出足球賽事的規則。
做法:按照題意模擬。

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int mp[N][N];
struct Player {
    int grade,score,id;
    bool operator <(const Player &rhs) const {
        if(grade>rhs.grade) return 1;
        else if(grade==rhs.grade&&score>rhs.score) return 1;
        else return 0;
    }
}a[N];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            scanf("%d",&mp[i][j]);
        }
    }
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            if(j==i) continue;
            if(mp[i][j]>mp[j][i]) a[i].grade+=3;
            else if(mp[i][j]==mp[j][i]) a[i].grade+=1;
            a[i].score+=mp[i][j]-mp[j][i];
        }
        a[i].id=i;
    }
    sort(a+1,a+1+n);
    if(n==1) printf("%d\n",a[1].id);
    else {
        if(a[1].grade==a[2].grade&&a[1].score==a[2].score)
            puts("play-offs");
        else printf("%d\n",a[1].id);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章