Note
For the first sample, prices for ATK and DEF are
extremely high. Master Yang can buy 99 HP, then he can beat the monster with 1HP
left.
For the second sample, Master Yang is strong enough to beat the monster, so he doesn‘t need to buy anything.
暴力。
h2:每秒减少max(0,a1-d2)
h1:每秒减少max(0,a2-d1)
要求让h1比h2先减完。
先考虑一种特殊情况:
如果两人都是每秒减少0,那么此时增加a1,d1都是起反作用的,只能增加h1。
其他情况下,d1最多增加到a2(再大没有意义),a1最多增加到d2+h1(再大没有意义)。
那么我们枚举d1,a1的增加量(O(n^2) n=100),可以O(1)算出来要获胜h1增加多少(代码中这里也是枚举的。。),最后输出最小费用即可。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
using namespace std;
int h1,h2,a1,a2,d1,d2,hc,dc,ac;
int win(int h1,int h2,int a1,int d1,int a2,int d2)
{
int x1=max(0,a2-d1),x2=max(0,a1-d2);
if (x1==0&&x2!=0) return 1;
if (x2==0) return 0;
int k1=ceil((double)h1/x1),k2=ceil((double)h2/x2);
if (k1>k2) return 1;
return 0;
}
int main()
{
cin>>h1>>a1>>d1;
cin>>h2>>a2>>d2;
cin>>hc>>ac>>dc;
int ans=1000000;
if (win(h1,h2,a1,d1,a2,d2))
{
cout<<0<<endl;
return 0;
}
int k;
if (max(a2-d1,0)==0)
{
cout<<(d2+1-a1)*ac<<endl;
return 0;
}
k=ceil((double)h1/(a2-d1));
for (int d=0;d<=a2;d++)
for (int a=0;a<=200;a++)
{
if (win(h1,h2,a1+a,d1+d,a2,d2))
{
ans=min(ans,a*ac+d*dc);
continue;
}
for (int i=k+1;i<=100;i++)
{
int h=(i-1)*(a2-d1)+1-h1;
if (win(h1+h,h2,a1+a,d1+d,a2,d2))
{
ans=min(ans,h*hc+a*ac+d*dc);
continue;
}
}
}
cout<<ans<<endl;
return 0;
}
Alexandra has a paper strip with n numbers on it. Let‘s call them ai from
left to right.
Now Alexandra wants to split it into some pieces (possibly 1). For each piece of strip, it must satisfy:
-
Each piece should contain at least l numbers.
-
The difference between the maximal and the minimal number on the piece should be at most s.
Please help Alexandra to find the minimal number of pieces meeting the condition above.
Output
Output the minimal number of strip pieces.
If there are no ways to split the strip, output -1.
Note
For the first sample, we can split the strip into 3 pieces: [1,?3,?1],?[2,?4],?[1,?2].
For the second sample, we can‘t let 1 and 100 be on the same piece,
so no solution exists.
题解中的做法是单调队列:
使用一个单增队列和一个单减队列求出g[i],表示i可以由g[i]到i-l转移;
再用一个单增队列,求出f[i]表示前i个数最少分成f[i]段,f[i]=min(f[k]+1) (g[i]<=k<=i-l)
其实也可以用set来做(虽然慢一点,但是好写多了):
两个set:
s放a[i],dp放f[i]
每次插入a[i],如果s中max-min>S,那么就把最靠前a[j]的弹出,把他对应的f[j-1]也弹出(如果被放入的话);
如果i-j+1>=l就把f[i-j]插入。
f[i]就是dp这个set中最小的元素+1
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <set>
#define mp make_pair
#define pa pair<int,int>
#define M 100000+5
#define inf 0x3f3f3f3f
using namespace std;
int f[M],n,ss,l,a[M];
multiset<int> s,dp;
int main()
{
scanf("%d%d%d",&n,&ss,&l);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1,j=1;i<=n;i++)
{
s.insert(a[i]);
for (;*s.rbegin()-*s.begin()>ss;j++)
{
s.erase(s.find(a[j]));
if (i-j>=l)
dp.erase(dp.find(f[j-1]));
}
if (i-j+1>=l) dp.insert(f[i-l]);
if (dp.begin()==dp.end())
f[i]=inf;
else f[i]=*dp.begin()+1;
}
if (f[n]>=inf)
puts("-1");
else cout<<f[n]<<endl;
return 0;
}
首先1肯定放第一个,n肯定放最后一个:
1放中间就和他前面的模值相同了;n放中间后面mod n全是0。
然后可以发现除了1,4和质数之外,答案都是NO,因为合数(n-1)! mod n=n,也就是说至少有两个mod n=0。
那么质数如何求方案数?
用这样的式子:
i=2
i/(i-1) * (i+1)/i * (i+2)/(i+1)...*(i+k)/(i+k-1)=i+k
第i位是i/(i-1),就可以得到一个排列了。
可以证明每一位的i/(i-1)都是不同的。
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
int n;
int ni(int x,int n,int mod)
{
long long b=x,ans=1;
while (n)
{
if (n&1) ans=ans*b%mod;
b=b*b%mod;
n>>=1;
}
return (int)(ans%mod);
}
int main()
{
cin>>n;
if (n==1)
{
printf("YES\n1\n");
return 0;
}
if (n==4)
{
printf("YES\n1\n3\n2\n4\n");
return 0;
}
for (int i=2;i<n;i++)
if (n%i==0)
{
puts("NO");
return 0;
}
printf("YES\n1\n");
for (int i=2;i<n;i++)
printf("%d\n",(int)(1LL*i*ni(i-1,n-2,n)%n));
printf("%d\n",n);
return 0;
}
Automatic Bakery of Cyberland (ABC) recently bought an n?×?m rectangle table. To serve the diners, ABC placed seats around the table. The size
of each seat is equal to a unit square, so there are 2(n?+?m) seats in total.
ABC placed conveyor belts on each unit square on the table. There are three types of conveyor belts: "^", "<"
and ">". A "^" belt can bring
things upwards. "<" can bring leftwards and ">"
can bring rightwards.
Let‘s number the rows with 1 to n from top to bottom,
the columns with 1 to m from left to right. We consider
the seats above and below the top of the table are rows 0 and n?+?1 respectively.
Also we define seats to the left of the table and to the right of the table to be column 0 and m?+?1.
Due to the conveyor belts direction restriction there are currently no way for a diner sitting in the row n?+?1 to be served.
Given the initial table, there will be q events in order. There are two types of events:
-
"A x y" means, a piece of bread will appear at
row x and column y (we will denote such position
as (x,?y)). The bread will follow the conveyor belt, until arriving at a seat of a diner. It is possible that the bread gets stuck in an infinite
loop. Your task is to simulate the process, and output the final position of the bread, or determine that there will be an infinite loop.
-
"C x y c"
means that the type of the conveyor belt at (x,?y) is changed to c.
Queries are performed separately meaning that even if the bread got stuck in an infinite loop, it won‘t affect further queries.
Output
For each event of type "A", output two integers tx, ty in
a line, separated by a space, denoting the destination of (x,?y) is (tx,?ty).
If there is an infinite loop, you should output tx?=?ty?=??-?1.
Sample test(s)
output
0 4
-1 -1
-1 -1
0 2
0 2
Note
For the first sample:
If the bread goes from (2,?1), it will go out of the table at (1,?3).
After changing the conveyor belt of (1,?2) to "<",
when the bread goes from (2,?1) again, it will get stuck at "><",
so output is (?-?1,??-?1).
分块/线段树。
分块很好写:
m很小,所以按照n分为sqrt(n)块,维护在每一块中能走到哪里,修改也只修改所在块。
注意修改的时候对于这一块的最上层要特判一下,因为上面那层也许还没更新。
因此每一个询问复杂度O(n/sqrt(n))=O(sqrt(n));
每一个修改复杂度O(m*sqrt(n))
线段树是按照n分段的,每个节点x维护tree[x][i]:表示从(r,i)能走到(l,tree[x][i]),l和r分别是左右子树,然后修改就是区间合并区间查询的问题了。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define mp make_pair
#define M 100000+5
using namespace std;
pair<int,int> f[M][20];
char s[M][20];
int z,n,m,q,num[M],cnt;
void Modify(int x1,int x2)
{
for (int i=x1;i<=x2;i++)
{
for (int j=0;j<=m+1;j++)
f[i][j]=mp(i,j);
for (int j=1;j<=m;j++)
if (s[i][j]=='^')
f[i][j]=i==x1?mp(x1-1,j):f[i-1][j];
for (int j=1;j<=m;j++)
if (s[i][j]=='<')
f[i][j]=f[i][j-1];
for (int j=m;j;j--)
if (s[i][j]=='>')
f[i][j]=f[i][j+1];
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=m;i++)
f[0][i]=mp(0,i);
for (int i=1;i<=n;i++)
scanf("%s",s[i]+1);
z=sqrt(n)+1;
cnt=-1;
for (int i=1;i<=n;i+=z)
{
cnt++;
Modify(i,min(n,i+z-1));
for (int j=i;j<=min(n,i+z-1);j++)
num[j]=cnt;
}
while (q--)
{
char t[5];
int x,y;
scanf("%s%d%d",t,&x,&y);
if (t[0]=='A')
{
int xx=x,yy=y;
int ok=1;
while (xx>=1&&yy>=1&&yy<=m)
{
int nx=f[xx][yy].first,ny=f[xx][yy].second;
if (nx==xx&&ny>=1&&ny<=m)
{
ok=0;
puts("-1 -1");
break;
}
xx=nx,yy=ny;
}
if (ok)
printf("%d %d\n",xx,yy);
}
else
{
scanf("%s",t);
int fi=num[x]*z+1;
s[x][y]=t[0];
Modify(x,min(fi+z-1,n));
}
}
return 0;
}
感悟:
看来我线段树还是没学好,D有时间写一下。。