文章目录
- Problem A. Jhadgre的C语言程序
- Problem B. Wpremig的AH之战
- Problem C. Wpremig的三角形
- Problem D. Jhadgre的梯子
- Problem E. Jhadgre的合唱队形
- Problem F. Jhadgre的伤心地
- Problem G. Wpremig的称球问题
- Problem H. Jhadgre的回家之路
- Problem I. Jhadgre的小饼干
- Problem J. Jhadgre爬楼梯
- Problem K. Jhadgre的π
- Problem L. Wpremig's Niuniu
- Problem M. Wpremig和Jhadgre的藏宝图
Problem A. Jhadgre的C语言程序
注意是 helle
Problem B. Wpremig的AH之战
巴什博弈,分三种情况讨论。
m<=n时,此时先手一开始就能赢。
m>n且m%(n+1) == 0 时,此时先手必败。
m>n且m%(n+1) != 0 时,此时先手只要取m % (n + 1)。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int m, n;
int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
if (m % (n + 1) == 0)
puts("You are loser");
else if (n >= m)
{
for (int i = m; i < n; i ++)
printf("%d ", i);
printf("%d\n", n);
}
else
printf("%d\n", m % (n + 1));
}
return 0;
}
Problem C. Wpremig的三角形
将三角形剖分成宽为d的矩形,从高到低排序,那么我们肯定选择前L/d个矩形放在区域内最优,那么无限剖分矩形后,假设我们选择的最后一个矩形高度是H,那么对于高度大于H的三角形来说,肯定有一个属于该三角形的高度为H的矩形被选择了。那么对于所有高度大于H的三角形来说,高度为H的部分肯定都放在区域内了,即交点的高度都是H。
结论:
1.最短边放在x轴上;
2.放置在x轴上的边确定时,最优解满足所有三角形之间的交点在一条平行于x轴的直线上
有了上面的结论后,显然,三角形越高越容易被选择,所以三角形应该越高越好。
有了这个结论,二分交点高度就能很容易地解决这个问题
#include <cstdio>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const int maxn = 100010;
int T, cas = 1, n;
double r, a[maxn], b[maxn], c[maxn];
double area[maxn], height[maxn];
double f(double h)
{
double ret = 0;
for (int i=1;i<=n;i++)
if (h < height[i]) ret += a[i] * (1.0 - h / height[i]);
return ret;
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d%lf", &n, &r);
for (int i=1;i<=n;i++) scanf("%lf%lf%lf", &a[i], &b[i], &c[i]);
for (int i=1;i<=n;i++)
{
double p = (a[i] + b[i] + c[i]) / 2;
area[i] = sqrt(p * (p-a[i]) * (p-b[i]) * (p-c[i]));
height[i] = area[i] * 2 / a[i];
}
double low = 0, high = 1000000, mid;
while ( fabs(high - low) > eps )
{
mid = (low + high) / 2;
if (f(mid) <= r) high = mid;
else low = mid;
}
double ans = 0, h = mid;
for (int i=1;i<=n;i++)
if (h < height[i]) ans += a[i] * (1.0 - h / height[i]) * (height[i] - h) / 2;
printf("%.3lf\n", ans + h * r);
}
return 0;
}
Problem D. Jhadgre的梯子
送的,注意数据范围刚好比int大,不是INT_MAX
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long n,l;
cin>>n>>l;
long long ans = l;
for (int i = 0 ; i < n ; ++i) {
long long x;cin>>x;
if (x > ans) ans = x;
}
cout<<ans - l<<endl;
return 0;
}
Problem E. Jhadgre的合唱队形
本来想让树套树可以过的…然而题目时限和空间限制都比较尴尬,所以索性改成整体二分了。
先考虑如果只询问一次,那么假设询问的名次是k,我们可以二分答案mid,每次check就是遍历所有修改,建立树状数组,对于一个给[x,y]区间加入一个数z的操作,如果z>=mid,那就给[a,b]整个区间+1。最后查询区间的和,即这个区间中>=mid的数的数量。如果这个数量>=k则向上二分并记录答案,否则向下二分
我们可以发现最消耗时间的地方就是维护了,而维护的操作只和mid有关,跟具体的询问无关。所以考虑对询问进行处理,每次把所有需要“向上二分”的询问(即[mid+1,r])和“向下二分”的询问(即[l,mid])分别放进一个集合,当然同时也要将可能对它们造成影响的修改也跟随询问加入集合,然后分开递归继续操作。
对于询问,如果询问区间的和大于等于询问的名次则把这个询问分到“向上二分”的集合中,否则分到“向下二分”的集合中。
对于修改,如果一个修改所加入的数<=mid,那么对于已经认定答案在[mid+1,r]的询问一定是没有贡献的,所以只需要加到“向下二分”的集合中;
否则那么这次修改对于区间[l,mid]的询问一定是有贡献的。如果把答案在[l,mid]的询问所求的名次都减去1,则这个修改也只会对“向上二分”的集合中的询问有贡献,所以将这次修改加入“向上二分”的集合中。
至于其中数的个数用BIT维护一下就好了,区间更新+区间查询。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MAXN 50005
typedef long long LL;
using namespace std;
int n,m,id[MAXN],t1[MAXN],t2[MAXN],T=0;
LL ans[MAXN];
LL read(){
LL x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';c=getchar();
}
return x*f;
}
struct Node1{
int opt,a,b;
LL c;
}O[MAXN];
struct BIT{
LL c[MAXN];int sign[MAXN];
int lowbit(int x){return x&-x;}
void add(int pos,int x){
while(pos<=n){
if(sign[pos]!=T)c[pos]=0,sign[pos]=T;
c[pos]+=x,pos+=lowbit(pos);
}
}
LL query(int pos){
LL res=0;
while(pos>0){
if(sign[pos]!=T)c[pos]=0,sign[pos]=T;
res+=c[pos],pos-=lowbit(pos);
}
return res;
}
}c1,c2;
void ADD(int a,int b){
c1.add(a,1),c1.add(b+1,-1);
c2.add(a,a),c2.add(b+1,-b-1);
}
LL QUERY(int a,int b){
LL res=0;
res+=(b+1)*c1.query(b)-c2.query(b);
res-=a*c1.query(a-1)-c2.query(a-1);
return res;
}
void solve(int l,int r,int ansl,int ansr){
if(l>r)return;
if(ansl==ansr){
for(int i=l;i<=r;i++)
if(O[id[i]].opt==2)ans[id[i]]=ansl;
return;
}
int mid=(ansl+ansr)>>1;
int j=1,k=1;
T++;
for(int i=l;i<=r;i++){
int idx=id[i];
if(O[idx].opt==1){
if(O[idx].c<=mid)t1[j++]=idx;
else t2[k++]=idx,ADD(O[idx].a,O[idx].b);
}
else{
LL res=QUERY(O[idx].a,O[idx].b);
if(res>=O[idx].c)t2[k++]=idx;
else t1[j++]=idx,O[idx].c-=res;
}
}
--j,--k;
for(int i=1;i<=j;i++)id[l+i-1]=t1[i];
for(int i=1;i<=k;i++)id[l+j+i-1]=t2[i];
solve(l,l+j-1,ansl,mid),solve(l+j,r,mid+1,ansr);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++){
O[i].opt=read();
O[i].a=read();
O[i].b=read();
O[i].c=read();
id[i]=i;
}
solve(1,m,-n,n);
for(int i=1;i<=m;i++)
if(O[i].opt==2)
printf("%d\n",ans[i]);
return 0;
}
Problem F. Jhadgre的伤心地
递归,小dp,随便写。
#include <bits/stdc++.h>
using namespace std;
int n;
int a[100000];
int fun(int x){
if (x > n) return 0;
return min(fun(a[x]),fun(x + 5)) + 1;
}
int main()
{
scanf("%d",&n);
for (int i = 1 ; i <= n ; ++i)
scanf("%d",&a[i]);
cout<< fun(1) * 2<<endl;
return 0;
}
Problem G. Wpremig的称球问题
首先给出结论:若称y次需要找出质量不同的球并且知道轻重,最多能称的球数量是n = ( 3 k − 3 ) / 2 (3^k-3)/2 (3k−3)/2
证明思路如下,依次证明引理:
引理一:
如果有m个球或者是标准或者比标准重,以及n个球或者是标准或者比标准轻以及至少1个标准球,则k次称量能够确定是哪个球的充要条件是:
m
+
n
≤
3
k
m+n≤3^k
m+n≤3k
引理二:
如果有n个球不知道是标准重量还是更轻还是更重,以及至少1个标准球,则k次称量能够确定是哪个球的充要条件是:
n
≤
1
/
2
(
3
k
+
1
)
n≤1/2(3^k+1)
n≤1/2(3k+1)
引理三:
如果有n个球不知道是标准重量还是更轻还是更重,以及至少1个标准球,则k次称量能够确定是哪个球,以及这个球是更轻还是更重的充要条件是:
n
≤
1
/
2
(
3
k
−
1
)
n≤1/2(3^k−1)
n≤1/2(3k−1)
引理一的充分性证明思路是:
令
m
=
3
i
+
j
,
n
=
3
k
+
r
m=3i+j, n=3k+r
m=3i+j,n=3k+r。每边放i个可能重的,k个可能轻的,然后j,r=0,1,2总共9种可能,分类讨论一下可以递归解决。
必要性是显然的。
引理二的充分性证明思路是:
(
3
k
+
1
)
/
2
(3^k+1)/2
(3k+1)/2个球,第一次
(
3
k
−
1
+
1
)
/
2
(3^{k-1}+1)/2
(3k−1+1)/2个球放左边,
(
3
k
−
1
−
1
)
/
2
(3^{k-1}-1)/2
(3k−1−1)/2个球加上标准球放右边,还余下
(
3
k
−
1
+
1
)
/
2
(3^{k-1}+1)/2
(3k−1+1)/2个第一次称量结束,要么平衡可以用递归证明,要么不平衡用引理证明。
引理三的充分性证明思路同理,区别在于余下
(
3
k
−
1
−
1
)
/
2
(3^{k-1}-1)/2
(3k−1−1)/2个球。
引理三的必要性证明思路与上面那个问题二相同
引理二的比较性证明就麻烦一些,不过思路还是类似。如果第一次称量不平衡要利用引理一,平衡的话递归。
最终两个问题的证明都需要单独讨论第一次称量,因为没有标准球可以借,并且数目比引理中少一,然后就可以利用引理的结论,通过类似的思路证明。
#include <bits/stdc++.h>
using namespace std;
int main(){
int x,y;
while (~scanf("%d%d",&x,&y)){
if (x <= 2){
puts("No");
continue;
}
long long now = 3;
int k;
for (k = 2 ; k < 15 ; ++k){
now *= 3;
if ((now-3)/2 >= x) break;
}
if (k <= y) puts("Yes");
else puts("No");
}
return 0;
}
Problem H. Jhadgre的回家之路
显然如果按照"L"→"W"→"Q"的路径很难进行搜索因为要先到每个"W"再从"W"出发搜索到"Q"的最短路径,但是这条路可以想象成"L"→"W"←"Q",这样的话就不需要考虑"W"了。
所以两遍bfs,分别从"L"为起点和以"Q"为起点,然后对每个"W"遍历,从"L"和"Q"到这个点的最短路之和就是拿这把钥匙回寝室的最短路,求个最短的就可以了。
或者两遍bfs,分别从"L"和"Q"为起点做两次bfs,然后遍历整个地图对所有"W"判断最短路
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 2010;
const int INF = 0x3f3f3f3f;
int n, m;
int vis[MAXN][MAXN];
int ret[2][MAXN][MAXN];
char a[MAXN][MAXN];
int xx[4] = {1,-1,0,0};
int yy[4] = {0,0,1,-1};
void bfs(int xxx, int yyy, int d) {
queue< pair<int, int> > q;
q.push(make_pair(xxx, yyy));
vis[xxx][yyy] = 1;
ret[d][xxx][yyy] = 0;
while(!q.empty()) {
int tx = q.front().first;
int ty = q.front().second;
q.pop();
for (int i = 0;i < 4; ++i) {
int x = tx + xx[i];
int y = ty + yy[i];
if (x >= 0 && x < n && y >= 0 && y < m && a[x][y] != '#' && !vis[x][y]) {
ret[d][x][y] = ret[d][tx][ty] + 1;
vis[x][y] = 1;
q.push(make_pair(x, y));
}
}
}
}
int main() {
while (~scanf("%d %d", &n, &m)){
queue< pair<int, int> > q;
memset(ret, INF, sizeof(ret));
memset(vis, 0, sizeof(vis));
int sx, sy;
int tx, ty;
for (int i = 0;i < n; ++i) {
scanf("%s", a[i]);
for (int j = 0;j < m; ++j) {
if (a[i][j] == 'W') {
q.push(make_pair(i, j));
} else if (a[i][j] == 'L') {
sx = i;
sy = j;
} else if (a[i][j] == 'Q') {
tx = i;
ty = j;
}
}
}
bfs(sx, sy, 0);
memset(vis, 0, sizeof(vis));
bfs(tx, ty, 1);
int ans = INF;
while(!q.empty()) {
int x = q.front().first;
int y = q.front().second;
q.pop();
ans = min(ans, ret[0][x][y] + ret[1][x][y]);
}
printf("%d\n", ans);
}
return 0;
}
Problem I. Jhadgre的小饼干
送的题目,string.find()即可
#include <bits/stdc++.h>
using namespace std;
int main()
{
int N;scanf("%d",&N);
int ans = 0 ;
while (N--){
string x;cin>>x;
if (x.find("zailaiyihe") != string::npos){
ans++;
}
}
cout<<ans<<endl;
return 0;
}
Problem J. Jhadgre爬楼梯
两重for循环即可…注意取模不是 1 e 9 + 7 1e9+7 1e9+7是 1 e 8 + 7 1e8+7 1e8+7
#include <iostream>
#include <cstdio>
using namespace std;
const int mod = 100000007;
int dp[10100];
int main() {
int n;
dp[0] = 1;
for (int i = 1; i <= 10000; ++i) {
dp[i] = dp[i - 1];
for (int j = i - 2; j >= 0; j -= 2) {
dp[i] += dp[j];
dp[i] %= mod;
}
}
while (cin>>n)
cout << dp[n] << endl;
return 0;
}
Problem K. Jhadgre的π
BBP公式或者各种收敛方式都可以。
http://www.huanqiujiemi.com/7xqE25x.html
参考图…日本发行的一本书
#include <cstdio>
#include <cmath>
#include <iostream>
#define MAX_C 56000
int a = 10000, b, c = MAX_C, d, e, f[MAX_C + 1], g, n, ans, cnt;
using namespace std;
int main()
{
int pp;
scanf("%d", &n);
a = 10000;
b = d = e = g = ans = cnt = 0;
c = MAX_C;
for (; b - c; ) f[b++] = a / 5;
for (; d = 0, g = c * 2; c -= 14, ans = e + d / a, e = d % a, cnt++) {
if (cnt * 4 > n) break;
for (b = c; d += f[b]*a, f[b] = d % --g, d /= g--, --b; d *= b);
}
if (n % 4 == 0) cout << (ans / 1000);
else if (n % 4 == 1) cout << ((ans / 100) % 10);
else if (n % 4 == 2) cout << ((ans / 10) % 10);
else if (n % 4 == 3) cout << (ans % 10);
cout<<endl;
return 0;
}
Problem L. Wpremig’s Niuniu
英语阅读题
先用一个五维数组预处理出所有可能的得分,然后在求每一组数据的时候直接求和再/13即可。注意四舍五入。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
inline void read(int &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9')c = ch, ch = getchar();
while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();
if(c == '-')x = -x;
}
inline int min(int a,int b)
{return a<b?a:b;}
int ans[15][15][15][15][15];
int x,y,m,n,z,sum;
inline void init()
{
for(int a = 1;a <= 13;++ a)
for(int b = 1;b <= 13;++ b)
for(int c = 1;c <= 13;++ c)
for(int d = 1;d <= 13;++ d)
for(int e = 1;e <= 13;++ e)
{
if(a<5 && b<5 && c<5 && d<5 && e<5 && a+b+c+d+e <= 10)
ans[a][b][c][d][e] = 60;
else if(a>10 && b>10 && c>10 && d>10 && e>10)
ans[a][b][c][d][e] = 50;
else if((a==b && b==c && c==d) || (a==b && b==c && c==e) || (a==b && b==d && e==d) || (a==e && e==c && c==d) || (e==b && b==c && c==d))
ans[a][b][c][d][e] = 40;
else
{
x = min(a,10),y = min(b,10),m = min(c,10),n = min(d,10),z = min(e,10);
if((x+y+m)%10 == 0 || (y+m+n)%10 == 0 || (m+n+z)%10 == 0 || (x+y+n)%10 == 0 || (x+y+z)%10 == 0 || (y+m+z)%10 == 0 || (y+n+z)%10 == 0 || (x+n+z)%10 == 0 || (x+m+n)%10 == 0 || (x+m+z)%10 == 0)
{
sum = x+y+m+n+z;
if(sum%10 == 0)
ans[a][b][c][d][e] = 30;
else if(sum%10 < 7)
ans[a][b][c][d][e] = sum%10;
else
ans[a][b][c][d][e] = (sum%10)*2;
}
}
}
}
int main()
{
init();
int t;
read(t);
for(;t;--t)
{
sum = 0;
read(x),read(y),read(m),read(n);
for(register int i = 1;i <= 13;++ i)
sum += ans[x][y][m][n][i];
sum = (sum/13.0)+0.5;
printf("%d\n",sum);
}
return 0;
}
Problem M. Wpremig和Jhadgre的藏宝图
对图进行黑白染色
设黑色格子的数量为 black
设白色格子的数量为 white
设黑色格子的数值和为 sumblack
设白色格子的数值和为 sumwhite
设最后每个格子的数字都变为x则
black * x – sumblack = white * x – sumwhite
x = (sumblack – sumwhite) / (black– white)
1.当black ≠ white时 可以解出 x 再用网络流check
2.当black = white时 ,可以发现 对于一个合法的x, k>=x都是一个合法的解
因为black = white => (black + white) % 2 == 0 可以构造一层的满覆盖
所以可以二分x 然后用网络流check
对于check
建图:
如果点k为白,建边(s, k, x – v[k])
如果点k为黑,建边(k, t, x – v[k])
对相邻点u、v (u为白),建边 (u, v, inf)
判断是否满流。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int s=1690,t=1691;
const LL oo2=1e17,oo1=1e13,oo3=1e15;
int a[50][50],num[50][50],xx[]={0,0,1,-1},yy[]={1,-1,0,0},
fir[1700],ne[10010],to[10010],que[1700],f[1700],
m,n,tot;
LL w[10010];
bool ok(int x,int y)
{
return x>=1&&x<=n&&y>=1&&y<=m;
}
void init()
{
int i,j;
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
scanf("%d",&a[i][j]);
}
void add(int u,int v,LL x)
{
tot++;
ne[tot*2]=fir[u];
fir[u]=tot*2;
to[tot*2]=v;
w[tot*2]=x;
ne[tot*2+1]=fir[v];
fir[v]=tot*2+1;
to[tot*2+1]=u;
w[tot*2+1]=0;
}
bool find()
{
int hd=1,tl=1,i,u,v;
que[1]=s;
memset(f,0,sizeof(f));
f[s]=1;
while (hd<=tl)
{
u=que[hd++];
for (i=fir[u];i;i=ne[i])
if (w[i]&&!f[v=to[i]])
{
f[v]=f[u]+1;
que[++tl]=v;
}
}
return f[t];
}
LL dfs(int u,LL lim)
{
int i,v;
LL ret=0,x;
if (u==t) return lim;
for (i=fir[u];i&&ret<lim;i=ne[i])
if (w[i]&&f[v=to[i]]==f[u]+1)
{
x=dfs(v,min(lim-ret,w[i]));
ret+=x;
w[i]-=x;
w[i^1]+=x;
}
if (!ret) f[u]=0;
return ret;
}
bool ok(LL x)
{
int i,j,kk,x1,y1;
tot=0;
memset(fir,0,sizeof(fir));
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
if (i+j&1) add(s,num[i][j],x-a[i][j]);
else add(num[i][j],t,x-a[i][j]);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
if (i+j&1)
for (kk=0;kk<4;kk++)
if (ok(x1=i+xx[kk],y1=j+yy[kk]))
add(num[i][j],num[x1][y1],oo3);
while (find())
{
int xxx;
xxx=1;
while (dfs(s,oo2));
}
for (i=fir[s];i;i=ne[i])
if (w[i]) return 0;
return 1;
}
void solve1()
{
int i,j;
LL s0=0,s1=0,x;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
if (i+j&1) s1+=a[i][j];
else s0+=a[i][j];
x=s0-s1;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
if (a[i][j]>x)
{
printf("-1\n");
return;
}
if (!ok(x)) printf("-1\n");
else printf("%lld\n",(x*m*n-s0-s1)/2);
}
void solve0()
{
int i,j;
LL s0=0,s1=0,x,l,r,mid;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
if (i+j&1) s1+=a[i][j];
else s0+=a[i][j];
if (s0!=s1)
{
printf("-1\n");
return;
}
l=0;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
l=max(l,(LL)a[i][j]);
r=oo1;
while (l<r)
{
mid=(l+r)/2;
if (ok(mid)) r=mid;
else l=mid+1;
}
if (l==oo1) printf("-1\n");
else printf("%lld\n",(l*m*n-s0-s1)/2);
}
int main()
{
int T,i,j;
for (i=1;i<=40;i++)
for (j=1;j<=40;j++)
num[i][j]=i*40+j;
scanf("%d",&T);
while (T--)
{
init();
if (m*n&1) solve1();
else solve0();
}
}