2019ICPC南昌

    技术2022-07-11  119

    2019ICPC南昌 重现


    待补


    B. A Funny Bipartite Graph

    题解


    C. And and Pair

    题意:

    给定一个很大的数n的二进制表示s(也就是01串),计算满足条件的数对<i,j>的数量 条件: 0<=j<=i<=n i&n=i i&j=0

    其中&符号表示位运算与 答案对1e9+7取模

    数据范围:|s|<=1e5

    解法:

    很容易看出可以用数位dp来解 当s[i]=1的时候,i可以为0或者1,i=0时j=0/1,i=1时j=0 当s[i]=0的时候,i只能为0,i=0时j=0/1 当存在最高位限制的时候,i=0时j=0,此时j不能为1 详见代码

    需要注意的点是平常数位dp是从高位到低位dp,拆分数的时候一般把高位放在数组的右边 但是给定的01串高位在左边,低位在右边,需要改一下dp的顺序,或者将串翻转一下。

    ps: 似乎还有其他解法,组合数学什么的

    code:

    #include<bits/stdc++.h> using namespace std; #define int long long const int maxm=1e5+5; const int mod=1e9+7; char s[maxm]; int d[maxm]; int dfs(int len,int limit){//limit是i的limit,j不需要limit if(!len)return 1; if(!limit&&d[len]!=-1)return d[len]; int ans=0; if(s[len]==1){//i可0可1 if(limit){//10+limit,00+!limit,不能01 ans+=dfs(len-1,1)+dfs(len-1,0); }else{//10,00,01 ans+=dfs(len-1,0)*3; } }else{//i只能0 if(limit){//00+limit,不能01 ans+=dfs(len-1,1); }else{//00,01 ans+=dfs(len-1,0)*2; } } ans%=mod; if(!limit)d[len]=ans; return ans; } int solve(){ int len=strlen(s+1); reverse(s+1,s+1+len);//翻转一下,否则dp中串的访问顺序就反了 for(int i=1;i<=len;i++){//init d[i]=-1; s[i]-='0'; } return dfs(len,1); } signed main(){ int T;cin>>T; while(T--){ scanf("%s",s+1); int ans=solve(); cout<<ans<<endl; } return 0; }

    E. Bob’s Problem

    题意:

    给定n个点m条边的无向图,和一个整数k 每条边是黑色或者白色,且每条边有边权 现在你必须在图中选择一些边,最多选择k条白边, 要求选择的边能使得图连通 问选择出的边的最大边权和是多少 如果无解则输出-1

    数据范围:n<=5e4,m<=5e5

    解法:

    要使得边权和最大,因为黑边没有数量限制所以全选,并查集维护一下连通性,把白边存下来

    然后白边按边权从大到小排序 遍历白边,如果白边两端不连通则选定且k-=1,否则先不选 如果k次机会选完了还是不连通则无解 如果连通之后k还有剩余,则优先把剩下的边权大的选下来

    code:

    #include<bits/stdc++.h> using namespace std; #define int long long const int maxm=1e6+5; struct E{ int x,y,w; friend bool operator<(E a,E b){ return a.w>b.w; } }; vector<E>temp; int mark[maxm];//表示被选定的白边 int pre[maxm]; int n,m,k; int ffind(int x){ return pre[x]==x?x:pre[x]=ffind(pre[x]); } signed main(){ int T;cin>>T; while(T--){ cin>>n>>m>>k; temp.clear(); for(int i=1;i<=n;i++){ pre[i]=i; } int ans=0; for(int i=1;i<=m;i++){ int x,y,w,c;cin>>x>>y>>w>>c; if(c==0){//黑边全选 ans+=w; pre[ffind(x)]=ffind(y);//并查集维护图的连通性 }else{//白边存下来 temp.push_back({x,y,w}); } } sort(temp.begin(),temp.end()); int len=temp.size(); for(int i=0;i<len;i++){ mark[i]=0; } for(int i=0;i<len&&k;i++){ int x=ffind(temp[i].x); int y=ffind(temp[i].y); if(x!=y){ pre[x]=y; ans+=temp[i].w; mark[i]=1; k--; } } if(k){//如果k还有剩余,则贪心的把边权大的未选的选下 for(int i=0;i<len&&k;i++){ if(!mark[i]){ ans+=temp[i].w; k--; } } } int cnt=0; for(int i=1;i<=n;i++){ if(pre[i]==i){ cnt++; if(cnt>1)break; } } if(cnt!=1){ puts("-1"); }else{ cout<<ans<<endl; } } return 0; }

    G. Eating Plan

    题意:

    给定长度为n的排列a,其中a[i]=k表示第i个盘子中的食物重量为(k的阶乘)

    你的胃很奇怪,当吃了重量为x的食物的时候,剩下的食物质量为x

    转载请注明原文地址:https://ipadbbs.8miu.com/read-12518.html
    最新回复(0)