#2700 聚会
Description
Solution
设 $f[x][y][a][b]$ 表示有 $x$ 杯茶,其中 $y$ 杯事 红 茶,$a(a=0/1)$ 种茶一共连续喝了 $b$ 次。
$$f[i][j][0][1]=min\{f[i-1][j-1][1][1],f[i-1][j-1][1][2]\}+(n-i+1)*a[j] (j>=1)$$ $$f[i][j][0][2]=f[i-1][j-1][0][1]+(n-i+1)*a[j] (j>=2)$$ $$f[i][j][1][1]=min\{f[i-1][j][0][1],f[i-1][j][0][2]\}+(n-i+1)*b[i-j] (i-j>=1)$$ $$f[i][j][1][2]=f[i-1][j][1][1]+(n-i+1)*b[i-j] (i-j>=2)$$
Code
#include<bits/stdc++.h>
using namespace std;
long long n,m,f[1005][1005][2][3],a[1005],b[1005];
int main(){
cin>>n>>m;
for (int i=1;i<=m;i++){
a[i]=b[i]=1145141919810;
}
for (int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
if (y==0){
b[++a[0]]=x;
}else{
a[++b[0]]=x;
}
}
sort(a+1,a+1+m);
sort(b+1,b+1+m);
memset(f,127,sizeof(f));
f[0][0][0][1]=0;
f[0][0][0][1]=0;
f[0][0][0][2]=0;
f[0][0][1][1]=0;
f[0][0][1][2]=0;
for (int i=1;i<=n;i++){
for (int j=0;j<=i;j++){
if (j>=1){
f[i][j][0][1]=min(f[i-1][j-1][1][1],f[i-1][j-1][1][2])+(n-i+1)*a[j];
}
if (j>=2){
f[i][j][0][2]=f[i-1][j-1][0][1]+(n-i+1)*a[j];
}
if (i-j>=1){
f[i][j][1][1]=min(f[i-1][j][0][1],f[i-1][j][0][2])+(n-i+1)*b[i-j];
}
if (i-j>=2){
f[i][j][1][2]=f[i-1][j][1][1]+(n-i+1)*b[i-j];
}
}
}
long long ans=1145141919810;
for (int i=0;i<=n;i++){
ans=min(ans,min(min(f[n][i][0][1],f[n][i][0][2]),min(f[n][i][1][1],f[n][i][1][2])));
}
cout<<ans;
}
#4269 再见
Description
Solution
线性基模板题,次大值为最大值异或线性基内最小的元素
Code
#include<bits/stdc++.h>
using namespace std;
long long p[100];
void solve(long long x){
for (long long i=62;i>=0;i--){
if ((long long)x>>(long long)i){
if (!p[i]){
p[i]=(long long)x;
return;
}else{
x^=(long long)p[i];
}
}
}
}
long long n,x;
int main(){
cin>>n;
for (int i=1;i<=n;i++){
cin>>x;
solve(x);
}
long long ans=0,dd=11451419198102333;
for (long long i=62;i>=0;i--){
if (((long long)ans^(long long)p[i])>(long long)ans){
ans^=(long long)p[i];
}
if (p[i]>0){
dd=min(dd,p[i]);
}
}
cout<<ans<<" "<<(ans^dd);
}
#4247 挂饰
Description
Solution
同样事 DP,设 $f[i][j]$ 表示前 $i$ 个挂饰,剩下 $j$ 个钩子的最大喜悦值。
Code
#include<bits/stdc++.h>
using namespace std;
int n,f[2500][2500];
struct node{
int a,b;
};
node s[3000];
bool cmp(node a,node b){
return a.a>b.a;
}
int main(){
cin>>n;
for (int i=1;i<=n;i++){
cin>>s[i].a>>s[i].b;
}
sort(s+1,s+1+n,cmp);
memset(f,-63,sizeof(f));
f[0][1]=0;
for (int i=1;i<=n;i++){
for (int j=0;j<=n;j++){
f[i][j]=max(f[i-1][j],f[i-1][max(j-s[i].a,0)+1]+s[i].b);
}
}
int ans=0;
for (int i=0;i<=n;i++){
ans=max(ans,f[n][i]);
}
cout<<ans;
}
#4576 [Usaco2016 Open]262144
Description
Solution
区间 DP,设 $f[i][j]$ 表示第 i 个位置 $j$ 大小的数能组成的那一串数的右侧位置。
Code
#include<bits/stdc++.h>
using namespace std;
int n,f[60][300000],ans=0;
int main(){
cin>>n;
for (int i=1;i<=n;i++){
int x;
cin>>x;
f[x][i]=i+1;
}
for (int i=1;i<=59;i++){
for (int j=1;j<=n;j++){
if (!f[i][j]){
f[i][j]=f[i-1][f[i-1][j]];
}
if (f[i][j]){
ans=max(ans,i);
}
}
}
cout<<ans;
}
#2460 [BeiJing2011]元素
Description
相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔法矿石炼制法杖的技术。那时人们就认识到,一个法杖的法力取决于使用的矿石。
一般地,矿石越多则法力越强,但物极必反:有时,人们为了获取更强的法力而使用了很多矿石,却在炼制过程中发现魔法矿石全部消失了,从而无法炼制出法杖,这个现象被称为“魔法抵消” 。特别地,如果在炼制过程中使用超过一块同一种矿石,那么一定会发生“魔法抵消”。后来,随着人们认知水平的提高,这个现象得到了很好的解释。经过了大量的实验后,著名法师 Dmitri 发现:如果给现在发现的每一种矿石进行合理的编号(编号为正整数,称为该矿石的元素序号),那么,一个矿石组合会产生“魔法抵消“当且仅当存在一个非空子集,那些矿石的元素序号按位异或起来
为零。 (如果你不清楚什么是异或,请参见下一页的名词解释。 )例如,使用两个同样的矿石必将发生“魔法抵消”,因为这两种矿石的元素序号相同,异或起来为零。 并且人们有了测定魔力的有效途径,已经知道了:合成出来的法杖的魔力等于每一种矿石的法力之和。人们已经测定了现今发现的所有矿石的法力值,并且通过实验推算出每一种矿石的元素序号。
现在,给定你以上的矿石信息,请你来计算一下当时可以炼制出的法杖最多有多大的魔力。
Solution
贪心+线性基,按照魔力值从大到小排序,将编号插入线性基,如果无法插入则不统计,可以插入则累计魔力值。
Code
#include<bits/stdc++.h>
using namespace std;
long long p[100];
bool solve(long long x){
for (long long i=62;i>=0;i--){
if ((long long)x>>(long long)i){
if (!p[i]){
p[i]=(long long)x;
return true;
}else{
x^=(long long)p[i];
}
}
}
return false;
}
struct node{
long long x,y;
};
node s[1500];
bool cmp(node a,node b){
return a.y>b.y;
}
long long n,x,y,ans=0;
int main(){
cin>>n;
for (int i=1;i<=n;i++){
cin>>s[i].x>>s[i].y;
}
sort(s+1,s+1+n,cmp);
for (int i=1;i<=n;i++){
if (solve(s[i].x)){
ans+=s[i].y;
}
}
cout<<ans;
}
#3039 玉蟾宫
Description
有一天,小猫 rainbow 和 freda 来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地。
这片土地被分成 $N*M$ 个格子,每个格子里写着’R’或者’F’,R 代表这块土地被赐予了 rainbow,F 代表这块土地被赐予了 freda。
现在 freda 要在这里卖萌……它要找一块矩形土地,要求这片土地都标着’F’并且面积最大。
但是 rainbow 和 freda 的 OI 水平都弱爆了,找不出这块土地,而蓝兔也想看 freda 卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为$ S$,它们每人给你 $S$ 两银子。
Solution
裸的悬线法
#4543 [POI2014]Hotel 加强版
Description
一棵树 $n(n<100000)$ 个点,求三个点距离两两相等的种类数。
Solution
设 $f[x][y]$ 表示 $x$ 子树下深度为 $y$ 的点的个数,$g[x][y]$ 表示 $x$ 子树下的点对个数,满足到其 $LCA$ 的距离为 $d$ ,$LCA$ 深度为 $d-y$
$$f[x][y]=\sum f[s][a-1]$$ $$g[x][y]+=\sum g[s][a+1]+f[x][a]*f[y][a]$$ $$ans+=\sum f[x][y]*g[s][y+1]+g[x][y]*f[y][y-1]$$
其中 $s$ 为 $x$ 的子结点
但是这么直接 DP 时间复杂度是过高的,需要启发式合并优化
长链剖分后继承重儿子的结果,使用指针来平移
Code
#include<bits/stdc++.h>
#define MAXN 114514
using namespace std;
int last[MAXN*2],to[MAXN*2],nextt[MAXN*2],topp=0;
int fa[MAXN],d[MAXN],son[MAXN],s[1919810];
int *f[MAXN],*g[MAXN],*now=&s[1];
int n,ans=0;
void add(int a,int b){
nextt[++topp]=last[a];
to[topp]=b;
last[a]=topp;
}
void dfs1(int x){
for (int i=last[x];i;i=nextt[i]){
int t=to[i];
if (t==fa[x]){
continue;
}
fa[t]=x;
dfs1(t);
d[x]=max(d[x],d[t]+1);
if (d[t]>d[son[x]]){
son[x]=t;
}
}
}
void calc(int x){
f[x]=now;
now+=d[x]+1;
g[x]=now+d[x]+1;
now+=d[x]*2+2;
}
void dfs2(int x){
if (son[x]){
f[son[x]]=f[x]+1;
g[son[x]]=g[x]-1;
dfs2(son[x]);
}
f[x][0]=1;
ans+=g[x][0];
for (int i=last[x];i;i=nextt[i]){
int t=to[i];
if (t==fa[x]||t==son[x]){
continue;
}
f[t]=now;
calc(t);
dfs2(t);
for (int j=d[t];j>=0;j--){
if (j){
ans+=f[x][j-1]*g[t][j];
}
ans+=f[t][j]*g[x][j+1];
g[x][j+1]+=f[x][j+1]*f[t][j];
}
for (int j=0;j<=d[t];j++){
if (j){
g[x][j-1]+=g[t][j];
}
f[x][j+1]+=f[t][j];
}
}
}
int main(){
cin>>n;
for (int i=1;i<=n-1;i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs1(1);
calc(1);
dfs2(1);
cout<<ans;
}
#2396 神奇的矩阵
Description
给出三个行数和列数均为$N$ 的矩阵 $A$、$B$、$C$,判断 $A*B=C$ 是否成立。
Solution
随机化,同乘一个 $1*n$ 的随机矩阵判断
Code
#include<bits/stdc++.h>
using namespace std;
int n,a[1005][1005],b[1005][1005],c[1005][1005],d[1005];
int x[1005],y[1005],z[1005];
int main(){
srand(time(0));
while(cin>>n){
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
cin>>a[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
cin>>b[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
cin>>c[i][j];
}
}
for (int i=1;i<=n;i++){
d[i]=rand()*114514;
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
x[i]+=d[j]*b[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
y[i]+=x[j]*a[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
z[i]+=d[j]*c[i][j];
}
}
bool check=0;
for (int i=1;i<=n;i++){
if (y[i]!=z[i]){
check=1;
break;
}
}
if (check){
cout<<"No"<<endl;
}else{
cout<<"Yes"<<endl;
}
}
}
#1213 [HNOI2004]高精度开根
Description
晓华所在的工作组正在编写一套高精度科学计算的软件,一些简单的部分如高精度加减法、乘除法早已写完了,现在就剩下晓华所负责的部分:实数的高精度开 $m$ 次根。因为一个有理数开根之后可能得到一个无理数,所以这项工作是有较大难度的。现在要做的只是这项工作的第一步:只对自然数进行开整数次根,求出它的一个非负根,并且不考虑结果的小数部分,只要求把结果截断取整即可。程序需要根据给定的输入,包括需要开根的次数,以及被开根的整数;计算出它的非负根取整后的结果。
Code
py 大法好
m=int(input()) n=int(input()) l=0 r=1 while (r**m<=n): l=r r=r*2 while (l<=r): mid=(l+r)//2 if (mid**m<=n): l=mid+1 else: r=mid-1 for i in range(l-2,l+2): x=i**m if (x==n): print(i) break if (x>n): print(i-1) break
是我门外了吗,感觉题目都好变态(´இ皿இ`)
代码bug了
a[i]=b[i]=1145141919810;
1145141919810
要 素 察 觉