打了场新生赛,觉得自己不仅输在终点,还输在了起跑线。
a.把每一个数组分成任意段,每段异或起来,让异或和最大
解:简单发现a^b<=a+b,所以最好的方法就是每个数字都是一段,答案就是数组求个和
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; typedef long long ll; const int maxn=100+10; int main(){ int n; scanf("%d",&n); int num=0; for(int i=0;i<n;i++){ int x; scanf("%d",&x); num+=x; } printf("%d\n",num); return 0; }
b.在一个二维平面上,每个人都会沿着一个方向走,当撞在一起时,他会继承其他人的方向,每个人只能被继承一次
经典弹性碰撞问题,在不考虑每个物品最后走到哪里只考虑时间的时候,直接忽略碰撞即可。
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; int main(){ int n,m; scanf("%d%d",&n,&m); int maxx=0; for(int i=0;i<m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); if(z==1){ maxx=max(maxx,x); }else if(z==2){ maxx=max(maxx,n+1-y); }else if(z==3){ maxx=max(maxx,n+1-x); }else{ maxx=max(maxx,y); } } printf("%d\n",maxx); return 0; }
c.有个人有X的时间和Y的脑细胞,做第i题要死xi个脑细胞和消耗yi的时间,问最多可以做多少题
二维费用背包模板题,顺便维护一下方案数就可以了
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; typedef long long ll; const int mod=1e9+7; const int maxn=100+10; const int maxm=1e5+10; ll w[maxm]; ll v[maxm]; ll f[maxn][maxn]; ll dp[maxn][maxn]; int main(){ ll n,t,V; scanf("%lld%lld%lld",&n,&t,&V); for(ll i=1;i<=n;i++){ ll a,b,c; scanf("%lld%lld%lld",&a,&b,&c); w[i]=a+c; v[i]=b; } f[0][0]=1; for(ll i=0;i<=t;i++){ for(ll j=0;j<=V;j++){ dp[i][j]=-1; } } dp[0][0]=0; for(ll i=1;i<=n;i++){ for(ll j=t;j>=w[i];j--){ for(ll k=V;k>=v[i];k--){ if(dp[j-w[i]][k-v[i]]>=0){ if(dp[j-w[i]][k-v[i]]+1 >dp[j][k]){ dp[j][k]= dp[j-w[i]][k-v[i]]+1; f[j][k]=f[j-w[i]][k-v[i]]%mod; } else if(dp[j-w[i]][k-v[i]]+1==dp[j][k]){ f[j][k]=(f[j][k]+f[j-w[i]][k-v[i]])%mod; } } } } } ll maxx=0,maxnum=0; for(ll i=0;i<=t;i++){ for(ll j=0;j<=V;j++){ if(dp[i][j]>maxx){ maxx=dp[i][j]; maxnum=f[i][j]; }else if(dp[i][j]==maxx){ maxnum=(maxnum+f[i][j])%mod; } } } printf("%lld %lld\n",maxx,maxnum); return 0; }
d.给n个数,问有多少个连续的区间和是k的倍数,
前缀问题,维护每个余数在前缀中出现的次数即可
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; const int maxn=1e6+10; typedef long long ll; ll a[maxn]; ll b[maxn]; int main(){ int n,k; scanf("%d%d",&n,&k); ll num=0; ll sum=0; b[0]=1; for (int i=0;i<n;i++ ){ scanf("%lld",&a[i]); sum+=a[i]; sum=sum%k; num+=b[sum]; b[sum]++; } printf("%lld\n",num); return 0; }
e.给一个n和k,x=kkkkkkk....n个k,问x%1e9+7是多少(n<1e18,k<10
开始以为是找循环节,想着这个公式mod1e9+7循环节应该不是很长,写了个map发现炸了.......
解:
等比数列求和得到 :9x = q × (10n − 1)
设t = (M+1)/9 = 111111112
则 9t = M + 1 ≡ 1 (mod M) 两边乘以 x
则有 x ≡ 9tx = t × 9x = t × q × (10n − 1) (mod M)
因此算 t × q × (10n − 1) 就行了
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; typedef long long ll; const int mod=1e9+7; const ll t=111111112; ll n,k; ll pow2(ll a,ll n){ if(n==0)return 1; ll x=pow2(a,n/2); ll ans=x*x%mod; if(n%2==1)ans=ans*a%mod; return ans; } int main(){ scanf("%lld%lld",&n,&k); ll num=(t*(k*(pow2(10LL,n)-1+mod)%mod)%mod)%mod; printf("%lld\n",num); return 0; }
f.给一个分数的中缀表达式,求结果(分数形式的结果)
大模拟(分数类+中缀表达式转后缀表达式+计算后缀表达式)
g.把一个数组分成连续的任意段,每一段所有数异或之后相等
两种情况:1,全部数字异或之后为0,随便找个点分开即可。
2,全部数字异或不为0后,设异或和为num,那么分完后结果必然是奇数段,且每一段异或和为num,那么直接分成三段,找第一次前缀异或为num的地方,和再次之后异或为0的地方即可
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; typedef long long ll; const int maxn=1e6+10; int a[maxn],n; int main(){ scanf("%d",&n); int num=0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); num=num^a[i]; } if(n<2){ printf("-1\n"); return 0; } if(num==0){ printf("2\n"); printf("1"); }else{ int l=0,r=0; int num0=0; for(int i=1;i<=n;i++){ num0=num0^a[i]; if(num0==num && l==0){ l=i; } else if(l>0 && num0==0){ r=i; } } if(l>0&&r>l){ printf("3\n"); printf("%d %d",l,r); }else{ printf("-1"); } } return 0; }
h.比较有意思的一道题,是luogu p1362原题
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; typedef long long ll; const int maxn=30000; int l,r,cnt,anss; int ans[30000]; void build(int x,int y,int z){ ll a=x; a=a*a; int b=0; if(x>=l&&x<=r){ while(a){ b+=a%10; a/=10; } if(y*y==b) ans[++cnt]=x; } if(z>r) return; for(int i=0;i<4;i++) build(x*10+i,y+i,z*10); } int main(){ scanf("%d%d",&l,&r); build(0,0,1); sort(ans+1,ans+1+cnt); for(int i=1;i<=cnt;i++){ if(ans[i-1]!=ans[i]) anss++; } printf("%d",anss); return 0; }
i.给你一棵有根树,树上有n个节点,m个节点上有人,人在树上的代价是他的深度,每个人只可以往自己的父亲节点走,并且每个节点上只可以站一个人,问最小代价是多少
用左偏树(可合并堆)维护每个子树的最小代价代价即可。
看了题解说dfs序+线段树也可以。
可合并堆代码:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <vector> #include <cstdio> #include <queue> #include <cmath> #include <map> #include <set> using namespace std; typedef long long ll; const int mod=1e9+7; const int maxn=5e5+10; const int maxm=2*maxn; //可合并堆部分 int tot,v[maxn],l[maxn],r[maxn],d[maxn]; int Mergr(int x,int y){ if(!x)return (y); if(!y)return (x); if(v[x]<v[y])swap(x,y); r[x]=Mergr(r[x],y); if(d[l[x]]<d[r[x]])swap(l[x],r[x]); d[x]=d[r[x]]+1; return (x); } int Init(int x){ tot++; v[tot]=x; l[tot]=r[tot]=d[tot]=0; return tot; } int Insert(int x,int y){ return Mergr(x,Init(y)); } int Top(int x){ return (v[x]); } int Pop(int x){ return Mergr(l[x],r[x]); } //树上dfs部分 int n,k; int vis[maxn]; int vistot[maxn]; vector<int>edges[maxn]; void addedge(int u,int v){ edges[u].push_back(v); } int numm=0; void dfs(int u,int fa,int deep){ for(int i=0;i<(int)edges[u].size();i++){ int v=edges[u][i]; if(v!=fa){ dfs(v,u,deep+1); } } vistot[u]=Init(deep); for(int i=0;i<(int)edges[u].size();i++){ int v=edges[u][i]; if(v!=fa){ vistot[u]=Mergr(vistot[u],vistot[v]); } } if(vis[u]==0){ vistot[u]=Pop(vistot[u]); numm++; } } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } for(int i=0;i<k;i++){ int x; scanf("%d",&x); vis[x]=1; } dfs(1,0,0); int num=0; for(int i=numm;i<n;i++){ num+=Top(vistot[1]); vistot[1]=Pop(vistot[1]); } printf("%d\n",num); return 0; }