本场难度:
简单:A,B,E,H,K
中等:C,D,F,G,I
签到题又双叒叕来了
线段树模板题,在建树时,把奇数位的数设为正数,偶数位置的数设为负数,在查询区间和的时候,判断 l l l 位置是正还是负,如果为负,只需要把区间和乘上-1即可。
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const int eps=1e-6;
int n,q;
int a[maxn];
ll tree[maxn<<2][2]; //0 偶数 1// 奇数
void build(int node,int l,int r){
if(l==r){
if(l%2==0){
tree[node][0]=a[l];
tree[node][1]=0;
}else{
tree[node][1]=a[l];
tree[node][0]=0;
}
return ;
}
int mid=(l+r)/2;
build(node<<1,l,mid);
build(node<<1|1,mid+1,r);
tree[node][0]=tree[node<<1][0]+tree[node<<1|1][0];
tree[node][1]=tree[node<<1][1]+tree[node<<1|1][1];
}
void update(int node,int pos,int val,int l,int r){
if(l==r){
if(l%2==0){
tree[node][0]=val;
}else{
tree[node][1]=val;
}
return ;
}
int mid=(l+r)/2;
if(mid>=pos) update(node<<1,pos,val,l,mid);
else update(node<<1|1,pos,val,mid+1,r);
tree[node][0]=tree[node<<1][0]+tree[node<<1|1][0];
tree[node][1]=tree[node<<1][1]+tree[node<<1|1][1];
}
ll query(int node,int l,int r,int L,int R,int id){
if(L<=l&&r<=R){
return tree[node][id];
}
int mid=(l+r)/2;
ll sum=0;
if(mid>=L) sum+=query(node<<1,l,mid,L,R,id);
if(mid<R) sum+=query(node<<1|1,mid+1,r,L,R,id);
return sum;
}
signed main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,1,n);
for(int i=1,opt,l,r,pos,val;i<=q;i++){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&l,&r);
ll ans=query(1,1,n,l,r,1)-query(1,1,n,l,r,0);
if(l%2==0) ans=-ans;
printf("%lld\n",ans);
}else{
scanf("%d%d",&pos,&val);
update(1,pos,val,1,n);
}
}
return 0;
}
大毒瘤
一维数组 f [ h i g h ] = l i f e f[high]=life f[high]=life
循环从d到0
如果这个高度的生命值不小于这个能源丢下来的时间
如果 高度加这个能源的高度不小于d,就输出这个能源丢下来的时间
不然 这个高度加这个能源的高度 的生命值=max(d~0的生命值),即不吃能源用它来堆,此时高度+=这个能源的高度
这个高度的生命值+=吃这个能源增长的生命值,即吃能源,此时高度不变
最后输出高度为0的生命值,即出不去存活的最长时间
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int mod=1e9+7;
const int MAXN=1e5+5;
struct node
{
int t;
int f;
int h;
}g[105];
int dp[1000];
bool cmp(node a,node b)
{
return a.t<b.t;
}
int main()
{
int d,n;
cin>>d>>n;
int sum=10;
for(int i=1;i<=n;i++)
{
cin>>g[i].t>>g[i].f>>g[i].h;
sum+=g[i].f;
}
sort(g+1,g+1+n,cmp);
memset(dp,-1,sizeof dp);
dp[0]=10;
for(int i=1;i<=n;i++)
{
for(int j=d;j>=0;j--)
{
if(dp[j]>=g[i].t)
{
if(j+g[i].h>=d)
{
cout<<g[i].t<<endl;
return 0;
}
dp[j+g[i].h]=max(dp[j+g[i].h],dp[j]);
dp[j]+=g[i].f;
}
}
}
cout<<dp[0]<<endl;
}
小YY闯地牢
C a b C_a^b Cab = b ! a ! ( a − b ) ! \frac {b!}{a! (a-b)!} a!(a−b)!b!
对这个式子取 l o g log log会变成: l o g b − l o g a − l o g ( a − b ) logb- loga-log(a-b) logb−loga−log(a−b)
因为花费是乘起来,但是再取完 l o g log log后,就会变成加起来,这样就变成普通的最短路,求组合数取模要么dp预处理,要么求逆元。
代码:
#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=2e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int head[MAXN];
int vis[MAXN];
ll dis[MAXN];
double pre[MAXN];
int cnt;
ll fac[MAXN];
double dp[MAXN];
void cal()
{
fac[0]=1;
for(int i=1; i<MAXN; i++)
fac[i]=fac[i-1]*i%mod;
}
ll qpow(ll a,ll b)
{
ll ans=1;
for(; b; b>>=1,a=a*a%mod)
if(b&1)
ans=ans*a%mod;
return ans;
}
ll C(ll n,ll m)
{
if(n<m)
return 0;
return fac[n]*qpow(fac[n-m]*fac[m]%mod,mod-2)%mod;
}
struct edge
{
int to;
double cost;
ll w;
int next;
}e[MAXN*2];
struct node
{
int pos;
double val;
friend bool operator<(node a,node b)
{
return a.val>b.val;
}
};
void add(int u,int v,ll w,double cost)
{
e[cnt].to=v;
e[cnt].w=w;
e[cnt].cost=cost;
e[cnt].next=head[u];
head[u]=cnt++;
}
void solve(int s,int k,int n)
{
for(int i=1;i<=n;i++)
{
vis[i]=0;
pre[i]=1e16;
dis[i]=1e16;
}
priority_queue<node> q;
q.push(node{
s,0});
pre[s]=0;
dis[s]=k;
while(!q.empty())
{
node now=q.top();
q.pop();
//cout<<now.pos<<endl;
if(vis[now.pos]) continue;
vis[now.pos]=1;
for(int i=head[now.pos];i!=-1;i=e[i].next)
{
int v=e[i].to;
//cout<<v<<" "<<pre[v]<<" "<<now.val+e[i].cost<<endl;
if(pre[v]>now.val+e[i].cost)
{
pre[v]=now.val+e[i].cost;
q.push(node{
v,pre[v]});
dis[v]=dis[now.pos]*e[i].w%mod;
}
}
}
}
int main()
{
cal();
for(int i=1;i<=1e3;i++)
{
dp[i]=dp[i-1]+log(i);
}
int t;
scanf("%d",&t);
while(t--)
{
int n,m,k,st,ed;
scanf("%d%d%d%d%d",&n,&m,&k,&st,&ed);
cnt=0;
for(int i=1;i<=n;i++)
{
head[i]=-1;
}
for(int i=1;i<=m;i++)
{
int u,v,a,b;
scanf("%d%d%d%d",&u,&v,&a,&b);
add(u,v,C(a,b),(dp[a]-dp[a-b])-dp[b]);
}
solve(st,k,n);
if(dis[ed]>=1e16) printf("-1\n");
else printf("%lld\n",dis[ed]);
}
}
温暖人心
二分最高等级,如果金币足够到达该等级,那么 l = m i d + 1 l=mid+1 l=mid+1,否则 r = m i d − 1 r=mid-1 r=mid−1
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int mod=1e9+7;
const int MAXN=1e5+5;
ll n[5],e[5];ll m,p,c,s;
bool check(ll mid)
{
ll res=0;
if(p/n[1]<mid){
res+=(mid*n[1]-p)*e[1];
}
if(c/n[2]<mid){
res+=(mid*n[2]-c)*e[2];
}
if(s/n[3]<mid){
res+=(mid*n[3]-s)*e[3];
}
return res<=m;
}
int main()
{
int t;
cin>>t;
while(t--){
cin>>m>>p>>c>>s;
for(int i=1;i<=3;i++){
cin>>n[i];
}
for(int i=1;i<=3;i++){
cin>>e[i];
}
ll d=min(min(p/n[1],c/n[2]),s/n[3]);
ll l=d,r=1e12;
ll ans;
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid)){
ans=mid;
l=mid+1;
}
else{
r=mid-1;
}
}
printf("%lld\n",ans);
}
}
我才是签到题
先用并查集求出每一个联通块,后面就是普通的01背包问题。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int mod=1e9+7;
const int MAXN=1e5+5;
int fa[MAXN];
ll num1[MAXN],num2[MAXN];
std::vector<int> v;
ll dp[MAXN];
int finder(int x)
{
if(fa[x]==x) return x;
else return fa[x]=finder(fa[x]);
}
void combine(int x,int y)
{
x=finder(x);
y=finder(y);
if(x!=y){
fa[x]=y;
num1[y]+=num1[x];
num2[y]+=num2[x];
}
}
int main()
{
int n,m,p;
cin>>n>>m>>p;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++){
cin>>num1[i]>>num2[i];
}
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
combine(x,y);
}
for(int i=1;i<=n;i++){
if(finder(i)==i&&p>=num2[i]){
v.push_back(i);
}
}
for(auto i:v){
for(int j=p;j>=num2[i];j--){
dp[j]=max(dp[j],dp[j-num2[i]]+num1[i]);
}
}
printf("%lld\n",dp[p]);
}
暴力ac题
状压 d p dp dp,用1表示有士兵,0表示没有士兵,那么可以用二进制去表示出所有状态,再看这些二进制是否不存在连续相邻的1,并且当前状态与前状态的&值必须为0,并且1必须放在平原上。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=5e2+500;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int a[15][15];
int b[15];
ll dp[15][1<<13];
int rightt[1<<13];
bool judge1(int i)
{
if(((i<<1)&i)==0) return true;
else return false;
}
bool judge2(int i,int j)
{
if(i&j) return false;
else return true;
}
int main()
{
int n,m;
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(b,0,sizeof b);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
a[i][j]^=1;
b[i]=b[i]*2+a[i][j];
}
}
int cnt=0;
for(int i=0;i<=(1<<m)-1;i++)
{
if(judge1(i))
{
rightt[++cnt]=i;
}
}
memset(dp,0,sizeof dp);
for(int i=1;i<=cnt;i++)
{
if(judge2(rightt[i],b[1]))
{
dp[1][rightt[i]]=1;
//cout<<rightt[i]<<endl;
}
}
for(int i=2;i<=n;i++)
{
for(int j=1;j<=cnt;j++)
{
if(!judge2(rightt[j],b[i])) continue;
for(int k=1;k<=cnt;k++)
{
if(dp[i-1][rightt[k]]==0) continue;
if(judge2(rightt[j],rightt[k]))
{
dp[i][rightt[j]]=(dp[i][rightt[j]]+dp[i-1][rightt[k]])%mod;
}
}
}
}
ll ans=0;
for(int i=1;i<=cnt;i++)
{
ans=(ans+dp[n][rightt[i]])%mod;
}
printf("%lld\n",ans);
}
}
排序
用线段树维护区间和,即1的数量,那么在每次操作时,先求出区间的1的数量,因为排序,所以区间就能分为两段,一段为0,一段为1,那么就是线段树区间修改值即可。
代码:
#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=5e4+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
struct Node
{
int l,r;
int lazy;
int sum;
}node[MAXN<<2];
int a[MAXN];
void push_down(int num)
{
if(node[num].lazy==-1) return;
node[num<<1].lazy=node[num<<1|1].lazy=node[num].lazy;
node[num<<1].sum=(node[num<<1].r-node[num<<1].l+1)*node[num].lazy;
node[num<<1|1].sum=(node[num<<1|1].r-node[num<<1|1].l+1)*node[num].lazy;
node[num].lazy=-1;
}
void push_up(int num)
{
node[num].sum=node[num<<1].sum+node[num<<1|1].sum;
}
void build(int l,int r,int num)
{
node[num].l=l;
node[num].r=r;
node[num].lazy=-1;
if(l==r)
{
node[num].sum=a[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,num<<1);
build(mid+1,r,num<<1|1);
node[num].sum=node[num<<1].sum+node[num<<1|1].sum;
}
void updata(int l,int r,int val,int num)
{
if(node[num].l>=l&&node[num].r<=r)
{
node[num].lazy=val;
node[num].sum=(node[num].r-node[num].l+1)*val;
return;
}
push_down(num);
int mid=(node[num].l+node[num].r)>>1;
if(l<=mid)
{
updata(l,r,val,num<<1);
}
if(r>mid)
{
updata(l,r,val,num<<1|1);
}
push_up(num);
}
int query(int l,int r,int num)
{
if(node[num].l>=l&&node[num].r<=r)
{
return node[num].sum;
}
push_down(num);
int ans=0;
int mid=(node[num].l+node[num].r)>>1;
if(l<=mid)
{
ans+=query(l,r,num<<1);
}
if(r>mid)
{
ans+=query(l,r,num<<1|1);
}
return ans;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
build(1,n,1);
int op,l,r;
while(q--)
{
scanf("%d%d%d",&op,&l,&r);
int sum=query(l,r,1);
int len=r-l+1-sum;
if(op==0)
{
if(l<=l+len-1) updata(l,l+len-1,0,1);
if(l+len<=r) updata(l+len,r,1,1);
}
else
{
if(l<=l+sum-1) updata(l,l+sum-1,1,1);
if(l+sum<=r) updata(l+sum,r,0,1);
}
}
for(int i=1;i<=n;i++)
{
cout<<query(i,i,1)<<" ";
}
}
Thebest的数学题
所有数都可以拆成素数相乘,用素数筛求出 1 e 6 1e6 1e6以内所有的素数,再对1到 n n n 所有数质因子分解,这样可以求出 n ! n! n! 质因子分解后每个素数有多少次幂,再将所有次幂+1相乘即可,比如 24 = 2 ∗ 2 ∗ 2 ∗ 3 = 2 3 ∗ 3 1 24=2*2*2*3=2^3*3^1 24=2∗2∗2∗3=23∗31 ,那么答案就是 ( 3 + 1 ) ∗ ( 1 + 1 ) = 8 (3+1)*(1+1)=8 (3+1)∗(1+1)=8
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=1e6+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int prime[MAXN],cnt;
int isprime[MAXN];
int vis[MAXN];
void getprime(int n)
{
for(ll i=2;i<=n;i++)
{
if(!isprime[i])
{
prime[++cnt]=i;
isprime[i]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
isprime[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
int main()
{
getprime(1e6);
int n;
scanf("%d",&n);
if(n==1){
cout<<1<<endl;
return 0;
}
for(int i=2;i<=n;i++){
int temp=i;
while(temp!=1){
vis[isprime[temp]]++;
temp/=isprime[temp];
}
}
ll ans=1;
for(int i=1;i<=cnt;i++){
if(vis[prime[i]]){
ans=ans*(vis[prime[i]]+1)%mod;
}
}
cout<<ans<<endl;
}
小b想要知道
a ⨁ b = n a \bigoplus b=n a⨁b=n 可以变为 a = n ⨁ b a=n \bigoplus b a=n⨁b ,
把 n n n 转成二进制,可以发现,因为 a a a要比 b b b 小,那么 n n n 的最高位的1 和 b b b 最高位的1 位置必须一样,否则 a a a就会比 b b b 大,
已经保证 a ≤ b a \leq b a≤b , 那么其实就是求 b b b 有多少取值,因为 b < n b < n b<n ,所以 b的取值就是 n − 1 − 2 x + 1 n-1-2^x+1 n−1−2x+1 ,x为n最高位的1的位置
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int mod=1e9+7;
const int MAXN=1e6+5;
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
int temp=n;
int cnt=0;
while(n)
{
cnt++;
n>>=1;
}
int x=1<<(cnt-1);
int ans=(temp-1)-x+1;
printf("%d\n",ans);
}
}
会长认证签到题
a ⋅ b = c a \cdot b=c a⋅b=c 可以推出 a ⋅ b a \cdot b a⋅b % m o d mod mod= c c c % m o d mod mod
即( a a a% m o d mod mod ⋅ \cdot ⋅ b b b% m o d mod mod)% m o d mod mod= c c c% m o d mod mod
因为一个整数可以表示成 a = x ⋅ 1 0 n + y ⋅ 1 0 n − 1 + z ⋅ 1 0 n − 2 + . . . . . a=x \cdot 10^n+y \cdot 10^{n-1}+z \cdot 10^{n-2}+..... a=x⋅10n+y⋅10n−1+z⋅10n−2+.....
那么 a a a对 m o d mod mod取模的值就很容易求出来了,只需枚举星号是什么数,算出三个数取模后的值,看相乘是否相等即可。
代码:
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e6+5,inf=0x3f3f3f3f,mod=998244353;
const double eps=1e-10;
int d1,d2,d3;
char a[maxn],b[maxn],c[maxn<<1];
ull sum1,sum2,sum3;
ull cal(int x){
sum3=0;
for(int i=1;i<=d3;i++){
if(c[i]=='*'){
sum3=(sum3*10+x)%mod;
}else{
sum3=(sum3*10+c[i]-'0')%mod;
}
}
return sum3;
}
signed main(){
scanf("%d%d%d",&d1,&d2,&d3);
scanf("%s %s %s",a+1,b+1,c+1);
for(int i=1;i<=d1;i++){
sum1=(sum1*10+a[i]-'0')%mod;
}
for(int i=1;i<=d2;i++){
sum2=(sum2*10+b[i]-'0')%mod;
}
for(int i=0;i<=9;i++){
if(sum1*sum2%mod==cal(i)){
printf("%d\n",i);
break;
}
}
return 0;
}