A:训仓达人
先状态压缩把每个区间特别的仓鼠表示出来,然后进行一次区间DP,在区间DP的顺带判断一下是否会不用花费就可以了.
AC_Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 155;
const long long inf = 1e18+7;
int n,m;
long long val[155];
long long f[155][155];
long long b[150];
long long dp[155][155];
long long pre[155];
int main() {
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) {
scanf("%lld",&val[i]);
pre[i+1] = pre[i] + val[i];
b[i] = -1;
}
for(int i=0;i<m;i++) {
int c;
scanf("%d",&c);
b[c-1] = i;
}
for(int len=1;len<=n;len++) {
for(int i=0;i+len-1<n;i++) {
int j = i+len -1;
if(i == j) f[i][j] = (b[i] == -1? 0:(1<<b[i]));
else {
f[i][j] =(f[i][j-1]|f[j][j]);
}
}
}
for(int len = 1;len<=n;len++) {
for(int i = 0;i+len-1<n;i++) {
int j = i+len-1;
if(i==j) {
dp[i][j] = 0;
continue;
}
else {
dp[i][j] = inf;
}
for(int k=i;k<j;k++) {
int s1 = f[i][k];
int s2 = f[k+1][j];
if(((s1<<1)&s2) || ((s1>>1)&s2)) {
//printf("%d %d %d??\n", i,j,k);
dp[i][j] = min(dp[i][k] + dp[k+1][j],dp[i][j]);
}
else {
dp[i][j] = min(dp[i][k] + dp[k+1][j] + pre[j+1] - pre[i],dp[i][j]);
}
}
}
}
printf("%lld\n",dp[0][n-1]);
return 0;
}
B: PUBG 1v3
很简单的一道线段树,比赛的时候 p公子 直接给我口胡成计算几何,题意都没有告诉我…
对于每个人能攻击的范围我们转化成一个矩阵,那么我们要的结果就是这个矩阵内的人数 - 自己队的人数,因为每队人数最多只有 4 个,我们完全可以先不管是否是本队的人(甚至包括他自己),等我们求出矩阵人数后直接暴力减去就可以了.
对于矩阵内人数的求解,我们可以参照扫描线的思想,将这个矩阵转化成两个新的矩阵之差,这两个矩阵拥有同样的底边,如下图.
所以s1中的个数就是s1 +s2 中的个数 - s2 中的个数,由此我们可以联想到前缀和,所以我们用个线段树去存所有区间的前缀和情况,用线段树查询就可以了.
Ac_Code
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e6+7;
struct node
{
int x,y,d,team,nex;
}s[maxn];
struct Q
{
int x1,x2,y,f;
int id;
}deq[maxn<<2];
bool cmp(const Q& a,const Q& b) {
return a.y<b.y;
}
bool cmp1(const int& a,const int& b) {
return s[a].y<s[b].y;
}
int n,m,e;
int tr[(maxn+7)<<3];
int head[maxn];
int p[maxn];
int ans[maxn];
int end_ans[maxn];
void pushup(int num) {tr[num] = tr[num<<1] + tr[num<<1|1];}
void build(int l,int r,int num) {
if(l==r) {
tr[num] = 0;
return ;
}
int mid =(l+r) >>1;
build(l,mid,num<<1);
build(mid+1,r,num<<1|1);
pushup(num);
}
void modify(int l,int r,int num,int pos,int d) {
if(l==r) {
tr[num]+=d;
return ;
}
int mid = (l+r) >>1;
if(pos<=mid) modify(l,mid,num<<1,pos,d);
if(mid< pos) modify(mid+1,r,num<<1|1,pos,d);
pushup(num);
}
int quriy(int l,int r,int num,int le,int ri) {
if(le<=l && r<= ri) return tr[num];
int mid =(l+r) >>1;
int ans =0;
if(le<=mid) ans += quriy(l,mid,num<<1,le,ri);
if(mid< ri) ans += quriy(mid+1,r,num<<1|1,le,ri);
return ans;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%d%d%d%d",&s[i].x,&s[i].y,&s[i].d,&s[i].team);
p[i] = i;
s[i].x++;
s[i].y++;
s[i].nex = head[s[i].team];
head[s[i].team] = i;
deq[e].x1 = max(-maxn+1,s[i].x-s[i].d);
deq[e].x2 = min(maxn-1,s[i].x+s[i].d);
deq[e].y = max(-maxn+1,s[i].y - s[i].d)-1;
deq[e].id = i;
deq[e++].f = -1;
deq[e].x1 = max(-maxn+1,s[i].x-s[i].d);
deq[e].x2 = min(maxn-1,s[i].x+s[i].d);
deq[e].y = min(maxn-1,s[i].y + s[i].d);
deq[e].id = i;
deq[e++].f = 1;
}
build(1,maxn*2,1);
sort(deq,deq+e,cmp);
sort(p+1,p+n+1,cmp1);
int cnt = 1;
for(int i=0;i<e;i++) {
while(cnt<=n && s[p[cnt]].y <= deq[i].y) {
modify(1,maxn*2,1,s[p[cnt]].x+maxn,1);
cnt++;
//cout<<cnt<<endl;
}
ans[deq[i].id] += deq[i].f * quriy(1,maxn*2,1,deq[i].x1+maxn,deq[i].x2+maxn);
//printf("%d %d %d?\n", deq[i].id,ans[deq[i].id],quriy(1,maxn,1,deq[i].x1,deq[i].x2));
}
for(int i = 1;i<=n;i++) {
int tea = s[i].team;
int x = s[i].x;
int y = s[i].y;
int d = s[i].d;
for(int j = head[tea];j;j=s[j].nex) {
//cout<<"?"<<endl;
int xx = s[j].x;
int yy = s[j].y;
if(abs(xx-x) <= d && abs(yy-y) <=d) ans[i]--;
}
end_ans[tea] = max(end_ans[tea],ans[i]);
}
int q;
scanf("%d",&q);
while(q--) {
int x;
scanf("%d",&x);
printf("%d\n", end_ans[x]);
}
return 0;
}
C:简单计算题
所有可能数 对 y取模一定只有 y种结果,那么我们只需要看 前y+1 个数 的取模情况,如果y+1个数内没有数模 y 为 0,那么说明 后面一定没有数。
AC_Code
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int B,x,y;
int main() {
int t;
scanf("%d",&t);
while(t--) {
scanf("%d%d%d",&B,&x,&y);
int sum = 0;
int ans = 0;
do {
ans++;
sum = (sum*B+x)%y;
}
while(sum!=0 && ans <= (y+7));
printf("%d\n",ans>y+7? -1:ans);
}
return 0;
}
D:简单题
我们将边权分成均等的两部分,分给它连接的两个点,然后我们就成了选点问题,贪心即可,因为有奇数边权,我们先把所有值乘以 2,后面再除掉就可以了
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e4+7;
int n,m;
long long a[maxn];
int main() {
scanf("%d%d",&n,&m);
for(int i = 0;i<n;i++) {
scanf("%lld",&a[i]);
a[i] *= 2;
}
for(int i =0;i<m;i++) {
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
a[l-1] += c;
a[r-1] += c;
}
sort(a,a+n);
long long sum =0;
int t=1;
for(int i=n-1;i>=0;i--) {
//cout<<a[i]<<endl;
sum += a[i]*t;
t *= -1;
}
printf("%lld\n",sum/2);
return 0;
}
F:轰炸区
经典的矩阵快速幂+期望DP,可惜日天卡了浮点误差,一直过不了…
//附上标称
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<cstdlib>
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=(a)-1;i>=b;--i)
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fuck(x) cout<<'[' << #x << ' ' << x << ']' << endl;
using namespace std;
const int MX = 1e2 + 5;
const int MM = 22;
int N;
struct Mat {
long double x[MM][MM];
Mat() {memset (x, 0, sizeof (x) );}
void init() {rep (i, 0, N) x[i][i] = 1;}
void clr() {memset (x, 0, sizeof (x) );}
Mat operator* (const Mat& _A) const {
Mat ret;
rep (i, 0, N) rep (j, 0, N) rep (k, 0, N) {
ret.x[i][j] += x[i][k] * _A.x[k][j];
}
return ret;
}
} A, B;
int d[MX][MX], cnt[MM];
long double dp[MM], p[MM];
void pre_solve (int n) {
N = 0;
rep (i, 0, n) rep (j, 0, n) N = max (N, d[i][j] + 1);
rep (i, 0, n) rep (j, 0, n) cnt[d[i][j]]++;
rep (i, 0, N) p[i] = 1.0 * cnt[i] / (n * n);
rep (i, 1, N) p[i] /= 1 - p[0];
A.clr();
rep (i, 1, N - 1) A.x[i][i - 1] = 1;
A.x[N - 1][N - 1] = 1;
rep (i, 1, N) A.x[0][i - 1] = 1.0 * p[i];
A.x[0][N - 1] = 1;
B.x[N - 1][0] = 1 / (1 - p[0]);
}
Mat power (int n) {
Mat ret; ret.init();
while (n) {
if (n & 1) ret = ret * A;
A = A * A;
n >>= 1;
}
return ret;
}
double solve (int n, int m) {
pre_solve (n);
Mat ans = power (m) * B;
return ans.x[0][0];
}
double solve() {
int n, m;
cin >> n >> m;
rep (i, 0, n) rep (j, 0, n) cin >> d[i][j];
return solve (n, m);
}
int main() {
printf("%.3f\n", solve());
return 0;
}
G:丢手绢
每次暴力修改每两个数之和,用线段树维护最大值.
AC_Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
#include<bitset>
#include<cassert>
using namespace std;
const int maxn = 1e5+7;
const int inf = 2e9+7;
int n,k,m;
int a[maxn];
int pa[maxn][10];
int Max[maxn<<2];
int Min[maxn<<2];
void pushup(int num) {
Max[num] = max(Max[num<<1],Max[num<<1|1]);
Min[num] = min(Min[num<<1],Min[num<<1|1]);
}
void build(int l,int r,int num) {
if( l==r ) {
Max[num] = 0;
Min[num] = inf;
return ;
}
int mid = (l+r) >>1;
build(l,mid,num<<1);
build(mid+1,r,num<<1|1);
pushup(num);
}
void modify(int l,int r,int num,int pos,int tmp,int op) {
if(l==r) {
if(op==1) Min[num] = tmp;
else Max[num] = tmp;
return ;
}
int mid = (l+r) >>1;
if(pos<=mid) modify(l,mid,num<<1,pos,tmp,op);
if(mid< pos) modify(mid+1,r,num<<1|1,pos,tmp,op);
pushup(num);
}
int main() {
scanf("%d",&n);
for(int i=0;i<n;i++) {
scanf("%d",&a[i]);
}
scanf("%d%d",&m,&k);
build(1,n,1);
for(int i=0;i<n;i++) {
int mins=inf;
int maxs=0;
for(int j=1;j<=k;j++) {
int l = (i+j)%n;
pa[i][j] = a[i]+a[l];
//cout<<pa[i][j]<<endl;
mins = min(mins,pa[i][j]);
maxs = max(maxs,pa[i][j]);
}
//cout<<i<<' '<<mins<<' '<<maxs<<endl;
modify(1,n,1,i+1,mins,1);
modify(1,n,1,i+1,maxs,2);
}
while(m--) {
int op;
scanf("%d",&op);
if(op == 2) {
printf("%d %d\n",Max[1],Min[1]);
}
else {
int pos,x;
scanf("%d%d",&pos,&x);
a[pos-1] = x;
for(int i=pos-1-k;i<=pos-1;i++) {
int mins = inf;
int maxs = 0;
for(int j =1;j<=k;j++) {
int l = (i+j)%n;
pa[i][j] = a[i] + a[l];
mins = min(mins,pa[i][j]);
maxs = max(maxs,pa[i][j]);
}
modify(1,n,1,i+1,mins,1);
modify(1,n,1,i+1,maxs,2);
}
}
}
return 0;
}
H:zhrt的数据结构课3
对于题目所给的树,我们求出直径 和到根节点的最长链.
最后结果等于 一棵树的直径 + (其他所有树最长链之和)*2,注意特判只有一个节点的树
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
#include<bitset>
#include<cassert>
using namespace std;
typedef long long LL;
LL INF = 1e18;
const LL maxn = 4e6 + 50;
LL ma[maxn], d[maxn];
struct Edge
{
LL to, next;
} edge[maxn * 2];
LL k, head[maxn];
void add(LL a, LL b){
edge[k].to = b;
edge[k].next = head[a];
head[a] = k++;
}
LL ins;
void dfs(LL u, LL pre, LL de){
LL flag = 1;
for(LL i = head[u]; i != -1; i = edge[i].next){
LL to = edge[i].to;
if(to == pre){
continue;
}
flag = 0;
dfs(to, u, de + 1);
}
if(flag){
ma[ins] = max(ma[ins], de);
}
}
queue<LL> que;
LL dis[maxn];
LL bfs1(LL s, LL num){
for(LL i = 1; i <= num; i++){
dis[i] = 0;
}
que.push(s);
dis[s] = 1;
LL MAX = 1;
LL node = s;
while(que.size()){
LL u = que.front();
que.pop();
for(LL i = head[u]; i != -1; i = edge[i].next){
LL to = edge[i].to;
if(!dis[to]){
dis[to] = dis[u] + 1;
if(dis[to] > MAX){
node = to;
MAX = dis[to];
}
que.push(to);
}
}
}
return node;
}
LL bfs2(LL s, LL num){
for(LL i = 1; i <= num; i++){
dis[i] = 0;
}
que.push(s);
dis[s] = 1;
LL MAX = 1;
while(que.size()){
LL u = que.front();
que.pop();
for(LL i = head[u]; i != -1; i = edge[i].next){
LL to = edge[i].to;
if(!dis[to]){
dis[to] = dis[u] + 1;
//printf("u = %lld dis[%lld] = %lld\n", u, to, dis[to]);
if(dis[to] > MAX){
MAX = dis[to];
}
que.push(to);
}
}
}
return MAX - 1;
}
int main() {
LL n;
scanf("%lld", &n);
LL maans = 0, mians = INF;
LL sum = 0;
int cnt = 0;
for(LL j = 1; j <= n; j++){
//prLLf("999\n");
ins++;
LL num;
scanf("%lld", &num);
k = 0;
for(LL i = 1; i <= num; i++){
head[i] = -1;
}
LL root;
for(LL i = 1; i <= num; i++){
LL x;
scanf("%lld", &x);
if(x == 0){
root = i;
} else{
add(x, i);
add(i, x);
}
}
dfs(root, 0, 0);
LL node = bfs1(root, num);
d[j] = bfs2(node, num);
sum += ma[j] * 2;
if(ma[j] == 0){
cnt++;
continue;
}
mians = min(ma[j] * 2 - d[j], mians);
maans = max(ma[j] * 2 - d[j], maans);
}
if(cnt == n){
printf("0 0\n");
} else{
printf("%lld %lld\n", sum - mians, sum - maans);
}
return 0;
}
G:本场最简单签到题
最终结果爆ull,我们需要自己写个输出
Ac_Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
#include<bitset>
#include<cassert>
using namespace std;
struct node
{
int a;
unsigned long long b;
} s[200005];
unsigned long long g[1000005];
bool vis[1000006];
int ans[70];
void getans(unsigned long long a,unsigned long long b,unsigned long long c) {
int d = 0;
for(int i = 65;i>=0;i--) {
ans[i] = (a%10 + b%10 + c%10 + d);
d = ans[i]/10;
ans[i] %= 10;
a/= 10;
b /= 10;
c/=10;
}
int flag = 0;
for(int i=0;i<66;i++) {
if(flag == 0 && ans[i] == 0) continue;
else if(flag == 0 && ans[i] != 0) {
flag = 1;
printf("%d",ans[i]);
}
else printf("%d",ans[i]);
}
if(flag == 0) printf("0\n");
else printf("\n");
}
int main() {
int t;
scanf("%d",&t);
while (t--)
{
memset(g,0,sizeof(g));
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i)
{
scanf("%d",&s[i].a);
}
for (int i = 0; i < n; ++i)
{
scanf("%lld",&s[i].b);
}
int max1=0;
for (int i = 0; i < n; ++i)
{
if(g[s[i].a] == 0){{
g[s[i].a] = s[i].b;
max1=max(max1,s[i].a);
}
} else{
g[s[i].a] = min(s[i].b, g[s[i].a]);
}
}
int ans = 0;
for(int i=0;i<=max1;++i)
{
if (g[i]>0){
ans++;
}
}
if (ans<=2)
{
printf("-1\n");
continue;
}
unsigned long long min1,min2,min3;
min1=min2=min3=9223372036854775807;
for(int i=0;i<=max1;++i)
{
if (g[i]>0)
{
if (g[i]<min1)
{
min3=min2;
min2=min1;
min1=g[i];
}else if (g[i]>=min1&&g[i]<min2)
{
min3=min2;
min2=g[i];
}
else if (g[i]>=min2&&g[i]<min3)
{
min3=g[i];
}
}
}
//cout<<min1<<" "<<min2<<" "<<min3<<endl;
getans(min1,min2,min3);
}
return 0;
}
J:一个顶俩
对于每条备用边找这个边对应两个点的LCA,当碰到这个点的时候这条边就成了无用边.
然后树形DP即可.
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
#include<bitset>
#include<cassert>
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define mme(a,b) memset((a),(b),sizeof((a)))
#define fuck(x) cout<<"* "<<x<<"\n"
using namespace std;
typedef pair<int, int> pii;
typedef long long LL;
const int maxn =1e5+7;
int n,m,q,e,cnt;
int head[maxn];
int edge[maxn<<2];
int nex[maxn<<2];
int vis[maxn<<2];
int le[maxn],ri[maxn];
void add1(int l,int r) {
edge[++e] = r;
nex[e] = head[l];
head[l] = e;
}
int son[maxn],sz[maxn],dep[maxn],top[maxn],fath[maxn];
int tod[maxn];
int del[maxn];
int dfs1(int u,int f) {
dep[u] = dep[f]+1;
sz[u]=1;
for(int i=head[u];i;i=nex[i]) {
int v = edge[i];
if(v==f) continue;
sz[u]+=dfs1(v,u);
if(sz[v] > sz[son[u]]) son[u]=v;
}
return sz[u];
}
int dfs2(int u,int f,int up) {
// cout<<u<<' '<<f<<' '<<up<<endl;
le[u] = ri[u] = ++cnt;
fath[u] = f;
top[u] = up;
if(son[u]) ri[u] = dfs2(son[u],u,up);
for(int i=head[u];i;i=nex[i]) {
int v=edge[i];
if(v==f||v==son[u]) continue;
ri[u] = dfs2(v,u,v);
}
return ri[u];
}
int lca(int u,int v) {
while(top[u]!=top[v]) {
// cout<<top[u]<<'!'<<top[v]<<endl;
if(dep[top[u]] >= dep[top[v]]) {
u = fath[top[u]];
}
else {
v = fath[top[v]];
}
}
if(dep[u] >= dep[v]) return v;
else return u;
}
int dp[maxn];
void getans(int u,int f) {
dp[u] = tod[u]-2*del[u];
for(int i = head[u];i;i=nex[i]) {
int v = edge[i];
if( v==f ) continue;
getans(v,u);
dp[u]+= dp[v];
}
return ;
}
int main() {
scanf("%d%d%d",&n,&m,&q);
for(int i = 0;i<n-1;i++) {
int l,r;
scanf("%d%d",&l,&r);
add1(l,r);
add1(r,l);
}
dfs1(1,0);
dfs2(1,0,1);
for(int i=0;i<m;i++) {
int l,r;
scanf("%d%d",&l,&r);
tod[l]++;
tod[r]++;
int c = lca(l,r);
del[c]++;
}
getans(1,0);
while(q--) {
int num;
scanf("%d",&num);
int l = edge[num*2];
int r = edge[num*2-1];
if(dep[l] > dep[r] ) printf("%d\n",dp[l]);
else printf("%d\n",dp[r]);
}
return 0;
}