牛客网多校第六场
A:Singing Contest
模拟题,在胜者树上模拟即可,最优策略很容易,要赢的人选比对方最大的数还要大的而且尽量小的歌就行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define maxn 15 using namespace std; set <int> s[1<<maxn]; int now[1<<maxn]; int n; int main(){ int T,cas=0; scanf("%d",&T); while(T--){ scanf("%d",&n); cas++; for(int i=1;i<=(1<<n);i++){ s[i].clear(); now[i]=i; for(int j=1;j<=n;j++){ int x; scanf("%d",&x); s[i].insert(x); } } for(int i=1;i<=n;i++){ for(int j=1;j<=(1<<n);j+=(1<<i)){ set<int>::iterator a=s[now[j]].end(); a--; set<int>::iterator b=s[now[j+(1<<(i-1))]].end(); b--; set<int>::iterator x,y; x=s[now[j]].lower_bound(*b); y=s[now[j+(1<<(i-1))]].lower_bound(*a); if((*a)>(*b)){ s[now[j]].erase(x); } else{ s[now[j+(1<<(i-1))]].erase(y); now[j]=now[j+(1<<(i-1))]; } } } printf("Case #%d: %d\n",cas,now[1]); } return 0; }
C:Generation I
首先第一个性质,每个数字插入只有在第一个位置插入是有效的,后面插入这个数字等于没插。第二个性质,填出两个不同的结果只有可能是因为选的数字不一样或者这些数字第一次插入位置不一样
所以先从m中选k个数字,然后对这k个数字排列一下,最后将这k个数字插入到数组中任意位置(第一个位置一定要插)
所以有公式:C(m,1)*1!*C(n-1,0)+C(m,2)*2!*C(n-1,1)+......+C(m,k)*k!*C(n-1,k-1),k=min(n,m);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #include <string> #include <map> using namespace std; const long long mod=998244353; long long n,m; long long fac[1000010],inv[1000010]; int main() { int i,j; fac[0]=1; for(i=1;i<=1000005;i++) fac[i]=(fac[i-1]*i)%mod; inv[1]=1; for(i=2;i<=1000005;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod; inv[0]=1; int T; scanf("%d",&T); int cas=1; while(T--) { scanf("%lld%lld",&n,&m); long long minn=min(n,m); long long flag1=1,flag2=1; long long ans=0; for(i=1;i<=minn;i++) { //cout<<inv[i]<<" "<<inv[i-1]<<endl; flag1=(flag1*((m-i+1)%mod))%mod*inv[i]%mod; ans=(ans+flag1*flag2%mod*fac[i]%mod)%mod; flag2=(flag2*((n-i)%mod))%mod*inv[i]%mod; //cout<<flag1<<" "<<flag2<<endl; } printf("Case #%d: %lld\n",cas++,ans); } return 0; }
D:Bulbasaur
模拟题,给每个身体找一个最大匹配就好了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <stack> #include <set> #define LSON l,m,x<<1 #define RSON m+1,r,x<<1|1 using namespace std; const int MAX=1e6+5; int t,n,m,k; struct node{ int v,cost,nxt; }edge[MAX]; int no,cas,head[MAX]; long long ans; int ext=1e5+5; void addedge(int u,int v,int cost){ edge[no].v=v; edge[no].cost=cost; edge[no].nxt=head[u]; head[u]=no++; edge[no].v=u; edge[no].cost=cost; edge[no].nxt=head[v]; head[v]=no++; } int main(){ int i,j; scanf("%d",&t); while(t--){ no=0; memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&m,&k); for(i=0;i<k;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); addedge(a,b+ext,c); } ans=0; for(i=1+ext;i<=m+ext;i++){ int mx=0; for(j=head[i];j!=-1;j=edge[j].nxt) mx=max(mx,edge[j].cost); ans=ans+(long long)mx; } printf("Case #%d: %lld\n",++cas,ans); } return 0; }
F:Squirtle
树形DP,题解证明很明白。转移的过程就是根据操作符,选择左子树和右子树中的最大0和1来构造根的最大0和1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import java.math.BigInteger; import java.util.*; public class Main { static int tree[][]=new int[4010][3]; static BigInteger dp[][]=new BigInteger[4010][3]; static BigInteger mi[]=new BigInteger[4010]; static String str[]=new String[4010]; static int n,lim; static int num[]=new int[4010]; static int fa; static void init() { for(int i=1;i<=4000;i++) { tree[i][0]=tree[i][1]=-1; dp[i][0]=dp[i][1]=BigInteger.ZERO; } } static void dfs(int now) { if(now>=n) { dp[now][0]=dp[now][1]=BigInteger.ONE; num[now]=1; return; } int lson=tree[now][0]; int rson=tree[now][1]; dfs(lson); dfs(rson); num[now]=num[lson]+num[rson]; for(int i=0;i<16;i++) { if(str[now].charAt(i)=='0') continue; for(int j=0;j<4;j++) { BigInteger l0 = BigInteger.ZERO; BigInteger l1 = BigInteger.ZERO; BigInteger r0 = BigInteger.ZERO; BigInteger r1 = BigInteger.ZERO; if (j % 2 == 0) { l0 = dp[lson][0]; l1 = mi[num[lson]].subtract(l0); } else { l1 = dp[lson][1]; l0 = mi[num[lson]].subtract(l1); } if (j / 2 == 0) { r0 = dp[rson][0]; r1 = mi[num[rson]].subtract(r0); } else { r1 = dp[rson][1]; r0 = mi[num[rson]].subtract(r1); } BigInteger cur0 = BigInteger.ZERO; BigInteger cur1 = BigInteger.ZERO; int tmp = i; if (tmp % 2 == 0) cur0 = cur0.add(l0.multiply(r0)); else cur1 = cur1.add(l0.multiply(r0)); tmp >>= 1; if (tmp % 2 == 0) cur0 = cur0.add(l0.multiply(r1)); else cur1 = cur1.add(l0.multiply(r1)); tmp >>= 1; if (tmp % 2 == 0) cur0 = cur0.add(l1.multiply(r0)); else cur1 = cur1.add(l1.multiply(r0)); tmp >>= 1; if (tmp % 2 == 0) cur0 = cur0.add(l1.multiply(r1)); else cur1 = cur1.add(l1.multiply(r1)); dp[now][0] = dp[now][0].max(cur0); dp[now][1] = dp[now][1].max(cur1); } } } public static void main(String[] args) { Scanner cin = new Scanner(System.in); int T=cin.nextInt(); mi[0]=BigInteger.ONE; for(int i=1;i<=4000;i++) mi[i]=mi[i-1].multiply(BigInteger.valueOf(2)); for(int cas=1;cas<=T;cas++) { init(); n=cin.nextInt(); for(int i=1;i<n;i++) str[i]=cin.next(); lim=2*n-1; //System.out.println(n); for(int i=2;i<=lim;i++) { fa=cin.nextInt(); //System.out.println(fa); if(tree[fa][0]==-1) tree[fa][0]=i; else tree[fa][1]=i; } dfs(1); System.out.println("Case #" + cas + ": " + dp[1][1]); } } }
G:Pikachu
最大流等于最小割,由于是完全图所以最小割一定是s和t所连接的边权和取min,发现只要得到树上一点到其他点的距离和就好
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <iostream> #include <stack> #include <set> #include <map> #define ll __int128 #define LSON l,m,x<<1 #define RSON m+1,r,x<<1|1 using namespace std; const int MAX=1e5+5; struct node{ int v,next,cost; }edge[MAX<<1]; int no,head[MAX],size[MAX]; long long f[MAX],g[MAX]; int t,n,cas=1; ll ans; void init(void){ no=0; ans=0; memset(head,-1,sizeof(head)); memset(g,0,sizeof(g)); } void addedge(int u,int v,int cost){ edge[no].v=v; edge[no].cost=cost; edge[no].next=head[u]; head[u]=no++; edge[no].v=u; edge[no].cost=cost; edge[no].next=head[v]; head[v]=no++; } void dfs1(int x,int fa){ f[x]=0,size[x]=1; for(int i=head[x];~i;i=edge[i].next){ int to=edge[i].v; if(to==fa) continue; dfs1(to,x); size[x]+=size[to]; f[x]+=f[to]+1LL*size[to]*edge[i].cost; } } void dfs2(int x,int fa){ for(int i=head[x];~i;i=edge[i].next){ int to=edge[i].v; if(to==fa) continue; g[to]+=g[x]+f[x]-f[to]-1LL*size[to]*edge[i].cost+1LL*(n-size[to])*edge[i].cost; dfs2(to,x); } } ostream& operator<<(std::ostream& os, __int128 T) { if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10)); return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ; } int main(){ int i; scanf("%d",&t); while(t--){ init(); scanf("%d",&n); for(i=0;i<n-1;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } dfs1(1,0); dfs2(1,0); for(i=1;i<=n;i++) f[i]+=g[i]; sort(f+1,f+n+1); for(i=1;i<=n;i++) ans+=f[i]*(n-i); printf("Case #%d: ",cas++); cout<<ans<<endl; } return 0; }
I:Team Rocket
喜闻乐见的数据结构题,但 分块 也能做。
将区间按左端点分块,块内再按右端点排序,记录每个块内的最小的左端点和最大的右端点。找到每次询问的x最后被哪个块包含,这个块之前的块可以每个块都进去找一遍,因为按右端点排序所以每个块内一定是从上往下删;而最后被包含的块要枚举块内每一个区间判断被哪些包含。每个区间只会被删一次,每次枚举的块的数量不超过sqrt(n),故总复杂度可以接受。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 3 #define maxn 200000+5 4 5 using namespace std; 6 7 struct Line{ 8 int l,r,pos; 9 }l[maxn]; 10 11 const int mod=998244353; 12 13 int minl[1000],maxl[1000],maxr[1000],ed[1000],st[1000],now[1000],bl[maxn],ans[maxn],fl[maxn]; 14 int n,m,L,T; 15 16 inline int in(){ 17 int x=0,f=1; 18 char ch=getchar(); 19 while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); } 20 while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); 21 return x*f; 22 } 23 24 bool cmp1(const Line &x,const Line &y){ 25 return x.l<y.l; 26 } 27 28 bool cmp2(const Line &x,const Line &y){ 29 return x.r>y.r; 30 } 31 32 void Build(){ 33 sort(l+1,l+1+n,cmp1); 34 L=sqrt(n); 35 if(n%L) T=n/L+1; 36 else T=n/L; 37 for(int i=1;i<=n;i++){ 38 fl[i]=ans[i]=0; 39 bl[i]=(i-1)/L+1; 40 } 41 for(int i=1;i<=n;i++) ed[bl[i]]=i; 42 for(int i=n;i>=1;i--) st[bl[i]]=i; 43 for(int i=1;i<=T;i++){ 44 now[i]=st[i]; 45 maxr[i]=-0x3f3f3f3f; 46 minl[i]= 0x3f3f3f3f; 47 maxl[i]=-0x3f3f3f3f; 48 } 49 for(int i=1;i<=n;i++){ 50 maxr[bl[i]]=max(maxr[bl[i]],l[i].r); 51 minl[bl[i]]=min(minl[bl[i]],l[i].l); 52 maxl[bl[i]]=max(maxl[bl[i]],l[i].l); 53 } 54 for(int i=1;i<=T;i++) 55 sort(l+st[i],l+ed[i]+1,cmp2); 56 } 57 58 int main(){ 59 int t,cas=0; 60 t=in(); 61 while(t--){ 62 n=in(); m=in(); cas++; 63 printf("Case #%d:\n",cas); 64 for(int i=1;i<=n;i++) 65 l[i].l=in(),l[i].r=in(),l[i].pos=i; 66 Build(); 67 int res=0,tmp; 68 for(int i=1;i<=m;i++){ 69 int x; 70 if(res!=0) x=in()^tmp; 71 else x=in(); 72 res=0; tmp=1; 73 for(int j=1;j<=T;j++){ 74 if(minl[j]>x) break; 75 if(maxr[j]<x) continue; 76 if(j==T || minl[j+1]>x){ 77 int flag=0; 78 for(int k=now[j];k<=ed[j];k++) 79 if(l[k].l<=x && l[k].r>=x){ 80 ans[l[k].pos]=i; tmp=(long long)tmp*l[k].pos%mod; 81 res++; fl[l[k].pos]=1; 82 } 83 int ll=-1,rr=0; 84 for(int k=now[j];k<=ed[j];k++) 85 if(!fl[l[k].pos]){ 86 if(ll==-1) ll=k; 87 rr=k-flag; 88 swap(l[k-flag],l[k]); 89 } 90 else flag++; 91 while(fl[l[now[j]].pos] && now[j]<=ed[j]) now[j]++; 92 while(fl[l[ed[j]].pos] && ed[j]>=now[j]) ed[j]--; 93 } 94 else{ 95 for(int k=now[j];k<=ed[j];k++){ 96 if(l[k].l<=x && l[k].r>=x){ 97 ans[l[k].pos]=i; tmp=(long long)tmp*l[k].pos%mod; 98 res++; now[j]++; 99 } 100 else break; 101 } 102 } 103 } 104 printf("%d\n",res); 105 } 106 for(int i=1;i<=n;i++) 107 printf("%d ",ans[i]); 108 puts(""); 109 } 110 return 0; 111 }
J:Heritage of skywalkert
开一开脑洞,觉得两个大数的lcm比一大一小的两个数的lcm大的概率很大,所以对于随机数剧中最大的lcm应该是在一堆大数之间。找出最大的几十个数,暴力求就行。这题数据大概取前15个大数就可以过了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; priority_queue <unsigned,vector<unsigned>,greater<unsigned> > q; unsigned x,y,z; unsigned orz[233]; int n; unsigned tang(){ unsigned t; x^=x<<16; x^=x>>5; x^=x<<1; t=x; x=y; y=z; z=t^x^y; return z; } unsigned gcd(unsigned a,unsigned b){ if(b==0) return a; return gcd(b,a%b); } int main(){ int T,cas=0; scanf("%d",&T); while(T--){ cas++; scanf("%d%u%u%u",&n,&x,&y,&z); for(int i=1;i<=n;i++){ unsigned a=tang(); if(!q.empty() && a<=q.top()) continue; q.push(a); if(q.size()>25){ q.pop(); } } int sz=q.size(); for(int i=1;i<=sz;i++) orz[i]=q.top(),q.pop(); unsigned long long ans=0; for(int i=1;i<=sz;i++) for(int j=i+1;j<=sz;j++){ unsigned long long k=(unsigned long long)orz[i]*orz[j]/gcd(orz[i],orz[j]); ans=max(ans,k); } printf("Case #%d: %llu\n",cas,ans); } return 0; }