Easy Chess 模拟 or dfs
恶心的签到题,但是如果用dfs写就还算一个正常的题目了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
struct node{
int x,y;
};
vector<node>ans;
bool ok;
bool vis[10][10];
void dfs(int x,int y,int sum){
// printf("x = %d y = %d sum = %d
", x,y,sum);
if(ok) return ;
if(vis[8][8]&&sum) return ;
if(sum==0){
if(x==8&&y==8){
int len = ans.size();
for(int i=0;i<len;i++) printf("%c%d ", ans[i].x+'a'-1,ans[i].y);
printf("
");
ok = true;
}
return ;
}
for(int i=1;i<=8;i++){
if(!vis[x][i]){
vis[x][i] = true;
ans.push_back(node{x,i});
dfs(x,i,sum-1);
ans.pop_back();
vis[x][i] = false;
}
if(!vis[i][y]){
vis[i][y] = true;
ans.push_back(node{i,y});
dfs(i,y,sum-1);
ans.pop_back();
vis[i][y] = false;
}
}
}
int main(){
int n;
scanf("%d",&n);
ok = false;
vis[1][1] = true;
ans.push_back(node{1,1});
dfs(1,1,n);
return 0;
}
PACM Team 简单的背包+优化
这个题目一看到肯定就是想直接dp,但是这个空间不太够,又因为这个答案很小,所以可以用short优化,再而因为背包记录路径可以不同pre数组,所以节约了空间,正好开五维的大小37的dp数组
#include <bits/stdc++.h>
using namespace std;
const int maxn = 37;
short dp[maxn][maxn][maxn][maxn][maxn];
struct node{
int p,a,c,m,g;
}e[maxn];
int res[maxn];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int p,a,c,m,g;
scanf("%d%d%d%d%d",&p,&a,&c,&m,&g);
e[i] = node{p,a,c,m,g};
}
int P,A,C,M;
scanf("%d%d%d%d",&P,&A,&C,&M);
int id = 0,ip = 0,ia = 0,ic = 0,im = 0,ans = 0;
for(int i=1;i<=n;i++){
for(int p=0;p<=P;p++){
for(int a=0;a<=A;a++){
for(int c=0;c<=C;c++){
for(int m=0;m<=M;m++){
dp[i][p][a][c][m] = dp[i-1][p][a][c][m];
if(p>=e[i].p&&a>=e[i].a&&c>=e[i].c&&m>=e[i].m){
if(dp[i-1][p-e[i].p][a-e[i].a][c-e[i].c][m-e[i].m]+e[i].g>dp[i][p][a][c][m]){
dp[i][p][a][c][m] = dp[i-1][p-e[i].p][a-e[i].a][c-e[i].c][m-e[i].m]+e[i].g;
}
}
if(dp[i][p][a][c][m]>ans){
ans = dp[i][p][a][c][m];
id = i,ip = p,ia = a,ic = c,im = m;
}
}
}
}
}
}
// printf("id = %d ans = %d
", id,ans);
int cnt = 0;
while(id>0){
if(dp[id][ip][ia][ic][im]==dp[id-1][ip][ia][ic][im]);
else{
res[++cnt] = id;
ip-=e[id].p,ia-=e[id].a,ic-=e[id].c,im-=e[id].m;
}
id--;
}
printf("%d
", cnt);
sort(res+1,res+1+cnt);
for(int i=1;i<=cnt;i++) printf("%d ", res[i]-1);
printf("
");
return 0;
}
Easy Problemset 签到题目
把题目读懂就行了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e2+5;
typedef long long ll;
vector<int>num[maxn];
int main(){
int n,k;
scanf("%d%d",&n,&k);
ll ans = 0;
for(int i=1;i<=n;i++){
int x,y;
scanf("%d",&x);
while(x--){
scanf("%d",&y);
num[i].push_back(y);
}
}
for(int j=0;j<maxn;j++){
for(int i=1;i<=n;i++){
if(num[i].size()<=j) ans+=50,k--;
else {
if(num[i][j]>=ans) ans+=num[i][j],k--;
}
if(!k) break;
// printf("i = %d k = %d now = %d ans = %lld
", i,j,num[i][j],ans);
}
if(!k) break;
}
printf("%lld
", ans);
return 0;
}
枚举每一个数,他可能都是那个底数gcd,所以就求他的质数倍,有多少个质数倍数小于等于n
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e7+5;
typedef long long ll;
int pri[maxn],cnt,v[maxn];
void init() {
cnt = 0;
memset(v,0,sizeof(v));
for (int i = 2; i < maxn; ++i) {
if (!v[i]) {
v[i] = i;
pri[cnt++] = i;
}
for (int j = 0; j < cnt; ++j) {
if (1ll * i * pri[j] >= maxn) break;
v[i * pri[j]] = pri[j];
}
}
}
int main(){
int n;
init();
scanf("%d",&n);
ll ans = 0;
for(int i=1;i<=n;i++){
ll cur = 0;
for(int j=0;j<cnt;j++){
if(1ll*i*pri[j]>n) break;
cur++;
}
ans+=cur*(cur-1);
}
printf("%lld
", ans);
}
Geometry Challenge splay
虽然说是splay模板题,但是完全想不到用splay来维护。
为什么可以用splay来维护呢,这个是因为对于这个序列我可把他转化成splay的中序遍历,用splay来维护这个中序遍历。
举例:12345,p=3,s=2
那么首先想让2变成splay的根节点,那么根节点左右两边的子树的中序遍历不会改变,因为要12放到34后面,所以此时的根节点2的右子树设为空,并且令2的右儿子变成根节点,所以现在就变成两棵树了,一棵树是只有12,一棵树有345,然后找到345这棵树的第2大的数4,把4变成根节点,此时12查到34后面5前面,所以让4的原来的右儿子5变成12这棵树的右儿子,12这棵树变成4的右儿子即可。
主要就是要理解splay维护序列的中序遍历。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
/*
splay 模板
支持:
1 插入节点
2 删除节点
3 求小于一个数的最大值
4 求大于一个数的最小值
5 求一个数处于存在的第几个位置
6 求第k个位置的值是多少
。。。
*/
struct Splay{
int fa[maxn],key[maxn],sub_size[maxn],sons[maxn][2],recy[maxn],root,whole_size;
void init(){
root = 0,whole_size = 0;
}
//清空一个节点
inline void clear(int x){
fa[x] = key[x] = recy[x] = sons[x][0] = sons[x][1] = sub_size[x] = 0;
}
int getWhich(int x){
return sons[fa[x]][1] == x;
}
inline void Update(int x){
if(x){
sub_size[x] = recy[x];
if(sons[x][0]) sub_size[x]+=sub_size[sons[x][0]];
if(sons[x][1]) sub_size[x]+=sub_size[sons[x][1]];
//先更新儿子节点,再更新父亲节点
}
}
void connect(int x,int y,int z){//x 连到 y 下面,关系是 z
if(y) sons[y][z] = x; // y存在,则y的z儿子是x
if(x) fa[x] = y; //x 存在,则x的父亲是y
}
void rotate(int x){
int father = fa[x],ffather = fa[father],m = getWhich(x),n = getWhich(father);
connect(sons[x][m^1],father,m);
connect(father,x,m^1);
connect(x,ffather,n);
Update(father),Update(x);
}
void splay(int x){
for(int father;father = fa[x];rotate(x)){
//splay 的双旋操作,还有点不太理解
if(fa[father]) rotate(getWhich(x)==getWhich(father)?father:x);
}
root = x;
}
void Insert(int x){
// printf("INSETR
");
if(!root){
root = ++whole_size;
key[root] = x;
recy[root] = sub_size[root] = 1;
sons[root][0] = sons[root][1] = 0;
return;
}
int now = root, father = 0;
while(true){
if(key[now]==x){
recy[now]++;
Update(now),Update(father);
splay(now);
return ;
}
father = now, now = sons[now][x>key[now]];
if(!now){
++whole_size;
key[whole_size] = x,fa[whole_size]=father;
recy[whole_size] = sub_size[whole_size] = 1;
sons[father][x>key[father]] = whole_size;
// printf("sons[%d][%d]=%d
", father,x>key[father],whole_size);
Update(father),splay(whole_size);
return ;
}
}
}
int findRank(int x){
int now = root,ans = 0;
while(true){
if(x<key[now]){
now = sons[now][0];
continue;
}
ans+=sub_size[sons[now][0]];
if(x==key[now]){
splay(now);
return ans+1;
}
ans += recy[now];
now = sons[now][1];
}
}
int findKth(int x){
int now = root;
while(true){
if(sons[now][0] && x<=sub_size[sons[now][0]]){
now = sons[now][0];
continue;
}
if(sons[now][0]) x-= sub_size[sons[now][0]];
if(x<=recy[now]) {
splay(now);
return key[now];
}
x-=recy[now];
now = sons[now][1];
}
}
//找到当前根节点的左子树中最大的节点
//其实就是找到小于当前根节点的最大值
int findPre(){
int now = sons[root][0];
while(sons[now][1]) now = sons[now][1];
return now;
}
//找打大于当前根节点的最小值
int findSuffix(){
int now = sons[root][1];
// printf("ss now=%d
", now);
while(sons[now][0]) now = sons[now][0];
return now;
}
void Delete(int x){
findRank(x);//主要为了把x这个值用findRank函数找到,然后旋转到根节点变成root,以便于后续的操作
if(recy[root]>1){
recy[root]--;
Update(root);
return ;
}
if(!sons[root][0]&&!sons[root][1]){
clear(root);
root = 0;
return ;
}
if(!sons[root][0]){
int old_root = root;
fa[root = sons[root][1]] = 0;
clear(old_root);
return ;
}
if(!sons[root][1]){
int old_root = root;
fa[root = sons[root][0]] = 0;
clear(old_root);
return ;
}
int old_root = root, left = findPre();
splay(left);
connect(sons[old_root][1],root,1);
clear(old_root);
Update(root);
}
void debug(int u,int pre,int z){
printf("u=%d pre=%d z=%d
",u,pre,z);
if(sons[u][0]) debug(sons[u][0],u,0);
if(sons[u][1]) debug(sons[u][1],u,1);
}
}splay;
int main(){
int n,m;
scanf("%d%d",&n,&m);
splay.init();
for(int i=1;i<=n;i++) splay.Insert(i);
while(m--){
int p,s;
scanf("%d%d",&p,&s);
if(p==1) continue;
int x = splay.findKth(p);
int u = splay.findPre();
splay.splay(u);
// printf("u = %d x = %d
", u,x);
// splay.debug(splay.root,0,0);
// printf("ssss
");
int v = splay.sons[u][1];
splay.fa[v] = 0,splay.root = v;
int now = splay.findKth(s);
int cur = splay.sons[now][1];
splay.sons[now][1] = u;
splay.fa[u] = now;
splay.sons[u][1] = cur;
splay.fa[cur] = u;
splay.Update(u);
splay.Update(now);
// splay.debug(splay.root,0,0);
// printf("ssss
");
}
for(int i=1;i<=n;i++) {
printf("%d", splay.findKth(i));
if(i==n) printf("
");
else printf(" ");
}
return 0;
}
Landscape Improved 思维+二分答案
题解:
二分一个数x,那么枚举每一个位置,对于位置 i ,他如果要成为一个边界,那么他可以对 之后的 i + x - a[i] 位置做贡献,可以对 i - x + a[i] 之前的位置做贡献。
这个做贡献指的是,比如 r,那么r 之后的数要砌 x 这么高的高度,那么在往前放格子,放到 i 这个位置之后,前面的就不需要放格子了。
#include <bits/stdc++.h>
#define lc (id<<1)
#define rc (id<<1|1)
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
int a[maxn],pre[maxn],last[maxn],el[maxn];
ll n,m,sum[maxn];
bool check(ll x){
for(int i=0;i<=n;i++) pre[i] = 0,last[i] = inf;
for(int i = 1;i <= n; i++){
int r = i + x - a[i];
if(r<=n) pre[max(r,i)] = i;
}
for(int i=n;i>=1;i--){
int l = i - x + a[i];
if(l>=1) last[min(l,i)] = i;
}
int l = 0;
for(int i=1;i<=n;i++){
l = max(l,pre[i]);
el[i] = l;
}
int r = inf;
for(int i=n;i>=1;i--){
r = min(r,last[i]);
l = el[i];
if(r>n||l<1) continue;
if(l == r) return true;
ll all = sum[r-1]-sum[l];
ll len1 = i - l - 1,len2 = r - i - 1;
ll cost = (x+x-len1)*(len1+1)/2+(x+x-len2)*(len2+1)/2-x;
if(cost-all<=m) return true;
}
return false;
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
sum[i] = sum[i-1]+a[i];
}
int l = 0,r = inf,ans = l;
while(l<=r){
int mid = (l+r)>>1;
if(check(mid)) ans = mid,l = mid+1;
else r = mid-1;
}
printf("%d
", ans);
return 0;
}