Problem 1004. Link with Equilateral Triangle
题意:
给定如下图所示的正三角形,左边的边不能填0, 右边的边不能填1, 下边的边不能填2,整个三角新只能填0,1,2,问对于给定边长的三角形是否有合法的填法。
题解:
对于一个合法的解,应当满足不存在同时包含0,1,2的三角形,下面我们证明这样的三角形一定存在。
左下角必然是1,右下角必然是0,底边不能含有2,则底边上必然有奇数条1-0的边,这些边都属于一个小三角形。考虑其他的0-1边,由于不在两个斜边上,其他的0-1边必然属于两个三角形。因此“每个三角形内0-1边的数量”的和必然为奇数。
但是,假设不存在0-1-2的三角形,则所有三角形都必然包含0条或2条的0-1边,产生了矛盾。
因此一定存在0-1-2的三角形。
代码:
(我都不想粘贴了)
#include<iostream>
using namespace std;
int n;
void solve(){
cin >> n;
cout << "No\n";
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
}
Problem 1006. BIT Subway
题意:
地铁折扣,官方打折的方案是:对于每个月,如果月初开始之前的购票总和加起来大于等于100,则以后的购票打八折,如果总和大于等于200,则打五折。
而现在有个大聪明认为,他可以将一张票拆成几部分来购买,前一部分凑满折扣,后一部分享受优惠。
现在给定若干张票的票价和购买顺序,要求输出大聪明认为的票价和官方票价。
题解:
模拟或者直接算最终的答案。
代码:
这是直接算答案的,模拟细心点就行了。
#include<iostream>
#include<iomanip>
using namespace std;
const int N = 1e5 + 10;
int n, a[N];
void solve(){
cin >> n;
double ans1 = 0, ans2 = 0, sum = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum += a[i];
if(ans2 < 100) ans2 += a[i];
else if(ans2 >= 100 && ans2 < 200) ans2 += 0.8 * a[i];
else ans2 += 0.5 * a[i];
}
if(sum < 100) ans1 = sum;
else if(sum >= 100 && sum < 225) ans1 = 100 + (sum - 100) * 0.8;
else ans1 = 200 + (sum - 225) * 0.5;
cout << ans1 << ' ' << ans2 << '\n';
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(3);
int t = 1;
cin >> t;
while(t--){
solve();
}
}
Problem 1011. Link is as bear
题意:
给定一个数列,可以做若干次操作,选择一个l和r,将l到r之间的数都变成这些数的异或和。问将这些数变成同一个数字,这个数字是多大。
题解:
线性基板子直接上
代码:
#include<iostream>
#define int long long
using namespace std;
int n, x, b[100];
void insert(int x){
for(int i = 62; i >= 0; i --){
if(x >> i){
if(b[i]) x ^= b[i];
else{
b[i] = x;
return ;
}
}
}
return;
}
void solve(){
cin >> n;
for(int i = 0; i <= 62; i++){
b[i] = 0;
}
for(int i = 1; i <= n; i ++){
cin >> x;
insert(x);
}
int ans = 0;
for(int i = 62; i >= 0; i--){
ans = max(ans, ans ^ b[i]);
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem 1007. Climb Stairs
题意:
有若干层塔,塔里有boss,每个boss都有一个血量。对于每一层,可以选择向上揍不超过k层,也可以选择向下一层。能够到达一层的条件是现有的攻击力大于等于boss血量。从0层出发,有一个初始攻击力x,问是否能通关。
题解:
首先,对于在往上k层之内且能往下刷完的,我们一定贪心的选择最低的这样一层,接下来就是查询问题。
我们定义pos为我们当前的位置,now为now一下已将刷完的最高位置,nxt数组为当前往下最多能刷到那里。那我们就从now + 1开始往上扫,能去我们就查询是否可以往下刷完,验证同时更新nxt,这样下次再来到这个点时,就可以直接跳过许多点,跟准确说,每个点就只会被经过两次。
代码:
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, x, k, a[N];
int sum[N], nxt[N];
void solve(){
cin >> n >> x >> k;
sum[0] = nxt[0] = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
nxt[i] = i;
}
int pos = 0, now = 0;
while(now < n){
bool flag = false;
for(int i = now + 1; i <= min(pos + k, n); i ++){
int tmp = i, xx = x;
while(xx >= a[tmp] && tmp > now){
xx += sum[tmp] - sum[nxt[tmp - 1]];
tmp = nxt[tmp - 1];
}
nxt[i] = tmp;
if(tmp <= now){
flag = true;
x += sum[i] - sum[now];
pos = now + 1;
now = i;
break;
}
}
if(!flag){
cout << "NO\n";
return;
}
}
cout << "YES\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
}
Problem 1001. Link with Bracket Sequence II
题意:
给定一个数列,正数表示一种括号的左半部分,负数表示该种括号的有半部分,0表示需要进行填补的。问有多少种不同的合法填补方案。
题解:
区间DP。
定义f(i,j)表示i到j为合法括号序且i和j配对的方案数;g(i,j)为i到j的合法括号序方案数。
那么当i和j位置可以匹配,则更新f(i,j) = k * g(i + 1,j - 1),其中k是i和j相匹配的方案数。
一般的,更新g(i,j)= g(i,k) * f(k + 1,j)
之所以需要f的定义,是因为单纯的区间DP会有重复的情况。
代码:
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N = 510;
const int mod = 1e9 + 7;
int n, m, a[N];
int f[N][N], g[N][N];
void solve(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j ++){
f[i][j] = g[i][j] = 0;
}
}
for(int i = 1; i <= n; i++){
cin >> a[i];
}
if(n % 2) {
cout << 0 << '\n';
return;
}
for(int i = 0; i <= n; i ++){
g[i + 1][i] = 1;
}
for(int len = 2; len <= n; len += 2){
for(int l = 1; l + len - 1 <= n; l ++){
int r = l + len - 1, tmp;
if(a[l] >= 0 && a[r] <= 0){
if(a[l] == 0 && a[r] == 0) tmp = m;
else if(a[l] == 0 || a[r] == 0) tmp = 1;
else if(a[l] + a[r] == 0) tmp = 1;
else tmp = 0;
f[l][r] = tmp * g[l + 1][r - 1] % mod;
}
for(int k = l; k <= r; k += 2){
g[l][r] = (g[l][r] + g[l][k - 1] * f[k][r]) % mod;
}
}
}
cout << g[1][n] << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem 1003. Magic
题意:
有若干座魔法塔,每一座运作都需要pi能量。塔有一个统一的半径,当对某一座塔加一点能量时,半径内的塔也会加一点能量。还有若干个限制条件,某一段的塔的总能量不能超过一个指定值,问最少需要加多少能量,或输出-1。
题解:
差分约束。
我们令a[i]为前i个魔法塔中加入的魔法原料之和。由于第i个魔法塔的魔法值只与有效半径k内的魔法原料数量有关,则魔法值需求pi可以表示为:
a[min(n, i + k - 1)] - a[max(0, i - k)] ≥ pi;
对于魔法原料添加的限制Lj,Rj,Bj可以表示为:
a[Rj] - a[Lj - 1] ≤ Bj;
对于每个能量塔中原料非负,需要满足:
a[i] = a[i - 1] ≥ 0;
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1e4 + 10;
const int mod = 1e9 + 7;
int n, k, q, top;
int p, l, r, b;
int head[N], dis[N], vis[N], cnt[N];
struct edge{
int to, cost, next;
}e[N * 3];
void init(){
top = 0;
for(int i = 0; i <= n+1; i++){
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
memset(head, -1, sizeof(head));
memset(dis, -1, sizeof(dis));
}
}
void add(int u, int v, int w){
e[top].to = v;
e[top].cost = w;
e[top].next = head[u];
head[u] = top ++;
}
bool spfa(int u){
queue<int> q;
q.push(u);
dis[u] = 0, vis[u] = 1, cnt[u] = 1;
while(!q.empty()){
u = q.front(); q.pop();
vis[u] = 0;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to, w = e[i].cost;
if(dis[v] < dis[u] + w){
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if(cnt[v] > n + 2) return false;
if(!vis[v]){
q.push(v);
vis[v] = 1;
}
}
}
}
return true;
}
void solve(){
init();
cin >> n >> k;
for(int i = 1; i <= n; i ++){
cin >> p;
add(max(0, i - k), min(n, i + k - 1), p);
add(i - 1, i, 0);
}
cin >> q;
for(int i = 1; i <= q; i++){
cin >> l >> r >> b;
add(r, l - 1, -b);
}
for(int i = 0; i <= n; i++) add(n + 1, i, 0);
if(!spfa(n + 1)) cout << -1 << '\n';
else cout << dis[n] << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
}
Problem 1002. Link with Running
题意:
一张图,经过一条边你会消耗一定的体力值和获得一定的健康值。
问在花费最小体力的前提下,获得最高的健康值。
题解:
首先用dijkstra跑出一条最短路,由于图不一定是DAG,因此用tarjan将强联通量缩起来,获得DAG后,跑最长路。
代码:
队友的,太长了不想写了
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<queue>
using namespace std;
#define debug(x) cout<<#x<<": "<<(x)<<endl
#define debug2(x,y) cout<<#x<<":"<<endl;for(int i=1;i<=y;++i)cout<<x[i]<<" ";cout<<endl
#define mem(x,y) memset(x,y,sizeof(x));
#define int long long
#define double long double
const int inf=0x3f3f3f3f3f3f3f3f;
const double eps=1e-9;
const double PI=acos(-1.0);
const int maxn=1e6+10;
struct Edge
{
int to,e,w;
bool operator<(const Edge &edge)const
{
return e<edge.e;
}
};
int n,m;
vector<Edge> edg[maxn];
vector<Edge> edg2[maxn];
vector<Edge> edg3[maxn];
int dis[maxn];
bool vis[maxn];
int tot=0,dfn[maxn],low[maxn];
int scc[maxn],scctot=0;
int tots=0,s[maxn];
bool ins[maxn];
int ind[maxn],ans[maxn];
int dp[maxn];
void dijkstra()
{
for(int i=1;i<=n;++i)
{
dis[i]=inf;
vis[i]=0;
}
priority_queue<Edge> q;
dis[1]=0;
q.push({1,0,0});
while(!q.empty())
{
int fr=q.top().to;
q.pop();
if(vis[fr])continue;
vis[fr]=1;
for(auto it:edg[fr])
{
if(dis[it.to]>dis[fr]+it.e)
{
dis[it.to]=dis[fr]+it.e;
q.push({it.to,-dis[it.to],0});
}
}
}
}
void tarjan(int fr)
{
dfn[fr]=low[fr]=++tot;
ins[fr]=1;
s[++tots]=fr;
for(auto it:edg2[fr])
{
if(dfn[it.to]==0)
{
tarjan(it.to);
low[fr]=min(low[fr],low[it.to]);
}
else if(ins[it.to])
low[fr]=min(low[fr],low[it.to]);
}
if(dfn[fr]==low[fr])
{
++scctot;
while(tots&&s[tots]!=fr)
{
scc[s[tots]]=scctot;
ins[s[tots]]=0;
--tots;
}
scc[s[tots]]=scctot;
ins[s[tots]]=0;
--tots;
}
}
void toposort()
{
queue<int> q;
for(int i=1;i<=scctot;++i)
if(ind[i]==0)q.push(i);
int tot=0;
while(!q.empty())
{
int fr=q.front();
q.pop();
ans[++tot]=fr;
for(auto it:edg3[fr])
{
if(--ind[it.to]==0)q.push(it.to);
}
}
for(int i=1;i<=scctot;++i)
dp[i]=-inf;
dp[scc[1]]=0;
for(int i=1;i<=tot;++i)
{
int fr=ans[i];
for(auto it:edg3[fr])
{
dp[it.to]=max(dp[it.to],dp[fr]+it.w);
}
}
}
void work()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
{
edg[i].clear();
edg2[i].clear();
edg3[i].clear();
ind[i]=dfn[i]=low[i]=scc[i]=0;
}
int fr,to,e,w;
for(int i=1;i<=m;++i)
{
cin>>fr>>to>>e>>w;
edg[fr].push_back({to,e,w});
}
dijkstra();
for(int fr=1;fr<=n;++fr)
{
// if(dis[fr]==inf)continue;
for(auto it:edg[fr])
{
if(dis[it.to]==dis[fr]+it.e)
edg2[fr].push_back(it);
}
}
tot=0;tots=0;scctot=0;
tarjan(1);
for(int fr=1;fr<=n;++fr)
{
for(auto it:edg2[fr])
{
if(scc[fr]!=scc[it.to])
{
auto t=it;
t.to=scc[it.to];
edg3[scc[fr]].push_back(t);
++ind[scc[it.to]];
}
}
}
toposort();
cout<<dis[n]<<" "<<dp[scc[n]]<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cout<<fixed<<setprecision(6);
int _=1;
cin>>_;
while(_--)
work();
return 0;
}