P1955 [NOI2015] 程序自动分析
题目描述
在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。
考虑一个约束满足问题的简化版本:假设 x1,x2,x3,⋯ 代表程序中出现的变量,给定 n 个形如 xi=xj 或 xi=xj 的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为:x1=x2,x2=x3,x3=x4,x4=x1,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。
现在给出一些约束满足问题,请分别对它们进行判定。
输入格式
输入的第一行包含一个正整数 t,表示需要判定的问题个数。注意这些问题之间是相互独立的。
对于每个问题,包含若干行:
第一行包含一个正整数 n,表示该问题中需要被满足的约束条件个数。接下来 n 行,每行包括三个整数 i,j,e,描述一个相等/不等的约束条件,相邻整数之间用单个空格隔开。若 e=1,则该约束条件为 xi=xj。若 e=0,则该约束条件为 xi=xj。
输出格式
输出包括 t 行。
输出文件的第 k 行输出一个字符串 YES 或者 NO(字母全部大写),YES 表示输入中的第 k 个问题判定为可以被满足,NO 表示不可被满足。
输入输出样例
输入 #1复制
2
2
1 2 1
1 2 0
2
1 2 1
2 1 1
输出 #1复制
NO
YES
输入 #2复制
2
3
1 2 1
2 3 1
3 1 1
4
1 2 1
2 3 1
3 4 1
1 4 0
输出 #2复制
YES
NO
说明/提示
【样例解释 1】
在第一个问题中,约束条件为:x1=x2,x1=x2。这两个约束条件互相矛盾,因此不可被同时满足。
在第二个问题中,约束条件为:x1=x2,x1=x2。这两个约束条件是等价的,可以被同时满足。
【样例说明 2】
在第一个问题中,约束条件有三个:x1=x2,x2=x3,x3=x1。只需赋值使得 x1=x2=x3,即可同时满足所有的约束条件。
在第二个问题中,约束条件有四个:x1=x2,x2=x3,x3=x4,x4=x1。由前三个约束条件可以推出 x1=x2=x3=x4,然而最后一个约束条件却要求 x1=x4,因此不可被满足。
【数据范围】
所有测试数据的范围和特点如下表所示:
| 测试点编号 | n 的规模 | i,j 的规模 | 约定 |
|---|---|---|---|
| 1 | 1≤n≤10 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 2 | 1≤n≤10 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 3 | 1≤n≤100 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 4 | 1≤n≤100 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 5 | 1≤n≤105 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 6 | 1≤n≤105 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 7 | 1≤n≤105 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 8 | 1≤n≤105 | 1≤i,j≤109 | 1≤t≤10e∈{0,1} |
| 9 | 1≤n≤105 | 1≤i,j≤109 | 1≤t≤10e∈{0,1} |
| 10 | 1≤n≤105 | 1≤i,j≤109 | 1≤t≤10e∈{0,1} |
实现代码:
cpp
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
using namespace std;
int t,n,f[1000007],book[1000007*3];
struct node{
int x,y,e;
}a[1000001];
bool cmp(node a,node b){
return a.e>b.e;
}
inline void first(int kkk){
for(int i=1;i<=kkk;i++) f[i]=i;
}
int get(int x){
if(x==f[x]) return x;
return f[x]=get(f[x]);
}
int main(){
scanf("%d",&t);
while(t--){
int tot=-1;
memset(book,0,sizeof(book));
memset(a,0,sizeof(a));
memset(f,0,sizeof(f));
int flag=1;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].e);
book[++tot]=a[i].x;
book[++tot]=a[i].y;
}
sort(book,book+tot);
int reu=unique(book,book+tot)-book;
for(int i=1;i<=n;++i){
a[i].x=lower_bound(book,book+reu,a[i].x)-book;
a[i].y=lower_bound(book,book+reu,a[i].y)-book;
}
first(reu);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
int r1=get(a[i].x);
int r2=get(a[i].y);
if(a[i].e){
f[r1]=r2;
}else if(r1==r2){
printf("NO\n");
flag=0;
break;
}
}
if(flag) printf("YES\n");
}
return 0;
}
P1884 [USACO12FEB] Overplanting S
题目描述
在一个笛卡尔平面坐标系里(则 X 轴向右是正方向,Y 轴向上是正方向),有 N (1≤N≤1000) 个矩形,第 i 个矩形的左上角坐标是 (x1,y1),右下角坐标是 (x2,y2)。问这 N 个矩形所覆盖的面积是多少?
注意:被重复覆盖的区域的面积只算一次。
输入格式
第一行,一个整数 N (1≤N≤1000)。
接下来有 N 行,每行描述一个矩形的信息,分别是矩形的 x1,y1,x2,y2(−108≤x1,y1,x2,y2≤108)。
输出格式
一个整数,被 N 个矩形覆盖的区域的面积。
输入输出样例
输入 #1复制
2
0 5 4 1
2 4 6 2
输出 #1复制
20
实现代码:
cpp
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 1010
using namespace std;
long long n,x[maxn],y[maxn],x2[maxn],y2[maxn],side[2*maxn];
int arr[maxn];
long long ans;
bool cmp(int a,int b)
{
return y[a]>y[b];
}
int main()
{
cin >> n;
for (int i=0;i<n;i++)
{
cin >> x[i] >> y[i] >> x2[i] >> y2[i];
side[2*i]=x[i];
side[2*i+1]=x2[i];
}
sort(side,side+2*n);
ans=0;
for (int i=1;i<2*n;i++)
{
if (side[i-1]==side[i]) continue;
int m=0;
for (int j=0;j<n;j++)
{
if (x[j]<=side[i-1] && side[i]<=x2[j]) arr[m++]=j;
}
sort(arr,arr+m,cmp);
long long h=y[arr[0]],d=y2[arr[0]],w=side[i]-side[i-1];
for (int j=1;j<m;j++)
{
int temp=arr[j];
if (y[temp]>d)
{
ans+=(h-y[temp])*w;
h=y[temp];
}
else
{
ans+=(h-d)*w;
h=y[temp];
}
if (y2[temp]<d) d=y2[temp];
}
ans+=(h-d)*w;
}
cout << ans << endl;
}
P2004 领地选择
题目描述
作为在虚拟世界里统帅千军万马的领袖,小 Z 认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小 Z 来说是非常重要的。
首都被认为是一个占地 C×C 的正方形。小 Z 希望你寻找到一个合适的位置,使得首都所占领的位置的土地价值和最高。
输入格式
第一行三个整数 N,M,C,表示地图的宽和长以及首都的边长。
接下来 N 行每行 M 个整数,表示了地图上每个地块的价值。价值可能为负数。
输出格式
一行两个整数 X,Y,表示首都左上角的坐标。保证最优解是唯一的。
输入输出样例
输入 #1复制
3 4 2
1 2 3 1
-1 9 0 2
2 0 1 1
输出 #1复制
1 2
说明/提示
对于 60% 的数据,N,M≤50。
对于 90% 的数据,N,M≤300。
对于 100% 的数据,1≤N,M≤103,1≤C≤min(N,M),每块地价值的绝对值不超过 32767。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int n,m,c;
const int N=1e3+10;
int a[N][N],hang[N][N];
long long sum[N][N];
int main(){
cin>>n>>m>>c;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
hang[i][j]=a[i][j]+hang[i][j-1];
sum[i][j]=hang[i][j]+sum[i-1][j];
}
}
long long X,Y,maxn=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if((i+c-1>n)||(j+c-1>m)) continue;
long long k=sum[i+c-1][j+c-1]+sum[i-1][j-1]-sum[i-1][j+c-1]-sum[i+c-1][j-1];
if(k>maxn){
maxn=k;
X=i;
Y=j;
}
}
}
cout<<X<<" "<<Y;
return 0;
}
P3017 [USACO11MAR] Brownie Slicing G
题目描述
Bessie 烤了一个长方形的布朗尼,可以看作是一个 R×C 的网格(1≤R≤500;1≤C≤500),由小方块组成。在第 i 行,第 j 列的方块中有 Nij(0≤Nij≤4,000)颗巧克力豆。
Bessie 想把布朗尼分成 A×B 块(1≤A≤R;1≤B≤C):每头牛一块。布朗尼的切割方式是先进行 A−1 次水平切割(总是在整数坐标上),将布朗尼分成 A 条带。然后每条带独立地进行 B−1 次垂直切割,也是在整数边界上。其他 A×B−1 头牛各自选择一块布朗尼,剩下最后一块给 Bessie。由于它们很贪心,它们会把巧克力豆最少的一块留给 Bessie。
假设 Bessie 以最优方式切割布朗尼,求 Bessie 能获得的最多巧克力豆数。
例如,考虑一个 5 行 4 列的布朗尼,巧克力豆分布如下:
1 2 2 1
3 1 1 1
2 0 1 3
1 1 1 1
1 1 1 1
Bessie 必须将布朗尼分成 4 条水平带,每条带有两块。Bessie 可以这样切割布朗尼:
1 2 | 2 1
---------
3 | 1 1 1
---------
2 0 1 | 3
---------
1 1 | 1 1
1 1 | 1 1
因此,当其他贪心的牛选择它们的布朗尼块时,Bessie 仍然可以得到 3 颗巧克力豆。
输入格式
* 第 1 行:四个用空格分隔的整数:R,C,A 和 B
* 第 2 行到第 R+1 行:第 i+1 行包含 C 个用空格分隔的整数:Ni1,...,NiC
输出格式
* 第 1 行:一个整数:Bessie 在她的布朗尼上能保证获得的最多巧克力豆数
显示翻译
题意翻译
输入输出样例
输入 #1复制
5 4 4 2
1 2 2 1
3 1 1 1
2 0 1 3
1 1 1 1
1 1 1 1
输出 #1复制
3
说明/提示
(由 ChatGPT 4o 翻译)
实现代码:
cpp
#include<iostream>
#include<cstdio>
using namespace std;
int r,c,a,b,map[501][501],s[501][501],ans;
bool check(int x)
{
int now=0,num=0;
for (int i=1;i<=r;i++)
{
int dis=0,sum=0;
for (int j=1;j<=c;j++)
if (dis+(s[i][j]-s[i][j-1])-(s[now][j]-s[now][j-1])<x)
dis+=(s[i][j]-s[i][j-1])-(s[now][j]-s[now][j-1]);
else
{
sum++;
dis=0;
}
if (sum>=b)
{
now=i;num++;
}
}
if (num<a) return 0;
return 1;
}
int main()
{
cin>>r>>c>>a>>b;
for (int i=1;i<=r;i++)
for (int j=1;j<=c;j++)
cin>>map[i][j];
for (int i=1;i<=r;i++)
for (int j=1;j<=c;j++)
s[i][j]=s[i-1][j]+s[i][j-1]+map[i][j]-s[i-1][j-1];
int h=0,t=s[r][c];
while (h<=t)
{
int mid=(h+t)/2;
if (check(mid))
{
h=mid+1;
ans=mid;
}
else t=mid-1;
}
cout<<ans;
}
P3406 海底高铁
题目描述
该铁路经过 N 个城市,每个城市都有一个站。不过,由于各个城市之间不能协调好,于是乘车每经过两个相邻的城市之间(方向不限),必须单独购买这一小段的车票。第 i 段铁路连接了城市 i 和城市 i+1(1≤i<N)。如果搭乘的比较远,需要购买多张车票。第 i 段铁路购买纸质单程票需要 Ai 博艾元。
虽然一些事情没有协调好,各段铁路公司也为了方便乘客,推出了 IC 卡。对于第 i 段铁路,需要花 Ci 博艾元的工本费购买一张 IC 卡,然后乘坐这段铁路一次就只要扣 Bi(Bi<Ai) 元。IC 卡可以提前购买,有钱就可以从网上买得到,而不需要亲自去对应的城市购买。工本费不能退,也不能购买车票。每张卡都可以充值任意数额。对于第 i 段铁路的 IC 卡,无法乘坐别的铁路的车。
Uim 现在需要出差,要去 M 个城市,从城市 P1 出发分别按照 P1,P2,P3,⋯,PM 的顺序访问各个城市,可能会多次访问一个城市,且相邻访问的城市位置不一定相邻,而且不会是同一个城市。
现在他希望知道,出差结束后,至少会花掉多少的钱,包括购买纸质车票、买卡和充值的总费用。
输入格式
第一行两个整数,N,M。
接下来一行,M 个数字,表示 Pi。
接下来 N−1 行,表示第 i 段铁路的 Ai,Bi,Ci。
输出格式
一个整数,表示最少花费。
输入输出样例
输入 #1复制
9 10
3 1 4 1 5 9 2 6 5 3
200 100 50
300 299 100
500 200 500
345 234 123
100 50 100
600 100 1
450 400 80
2 1 10
输出 #1复制
6394
说明/提示
2 到 3 以及 8 到 9 买票,其余买卡。
对于 30% 数据 M=2。
对于另外 30% 数据 N≤1000,M≤1000。
对于 100% 的数据 M,N≤105,Ai,Bi,Ci≤105。
实现代码:
cpp
#include<iostream>
using namespace std;
char ch;
template<class T>
inline void rd(T& x) {
x = 0; int w = 1;
ch = getchar();
while (ch < '0' || ch>'9') {
if (ch == '-')w = -1; ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
x = x * w;
}
int a[100001]; unsigned long long t ,sum;
int main()
{
int n, m,x,y,z;
rd(n), rd(m);
rd(x);
for (int i = 2; i <= m; i++)
{
rd(y);
if (x < y)
{
a[x]++;
a[y]--;
}
else
{
a[x]--;
a[y]++;
}
x = y;
}
for (int i = 1; i < n; i++)
{
t += a[i];
rd(x),rd(y),rd(z);
sum += t * y + z < t * x ? t * y + z : t * x;
}
cout << sum;
return 0;
}
P1083 [NOIP 2012 提高组] 借教室
题目描述
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来 n 天的借教室信息,其中第 i 天学校有 ri 个教室可供租借。共有 m 份订单,每份订单用三个正整数描述,分别为 dj,sj,tj,表示某租借者需要从第 sj 天到第 tj 天租借教室(包括第 sj 天和第 tj 天),每天需要租借 dj 个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 dj 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 sj 天到第 tj 天中有至少一天剩余的教室数量不足 dj 个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
输入格式
第一行包含两个正整数 n,m,表示天数和订单的数量。
第二行包含 n 个正整数,其中第 i 个数为 ri,表示第 i 天可用于租借的教室数量。
接下来有 m 行,每行包含三个正整数 dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从 1 开始的整数编号。
输出格式
如果所有订单均可满足,则输出只有一行,包含一个整数 0。
否则(订单无法完全满足)输出两行,第一行输出一个负整数 −1,第二行输出需要修改订单的申请人编号。
输入输出样例
输入 #1复制
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出 #1复制
-1
2
说明/提示
【输入输出样例说明】
第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第 2 个申请人修改订单。
【数据范围】
对于 10% 的数据,有 1≤n,m≤10;
对于 30% 的数据,有 1≤n,m≤1000;
对于 70% 的数据,有 1≤n,m≤105;
对于 100% 的数据,有 1≤n,m≤106,0≤ri,dj≤109,1≤sj≤tj≤n。
NOIP 2012 提高组 第二天 第二题
2022.2.20 新增一组 hack 数据
实现代码:
cpp
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m;
int diff[1000011],need[1000011],rest[1000011],r[1000011],l[1000011],d[1000011];
bool isok(int x)
{
memset(diff,0,sizeof(diff));
for(int i=1;i<=x;i++)
{
diff[l[i]]+=d[i];
diff[r[i]+1]-=d[i];
}
for(int i=1;i<=n;i++)
{
need[i]=need[i-1]+diff[i];
if(need[i]>rest[i])return 0;
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&rest[i]);
for(int i=1;i<=m;i++)scanf("%d%d%d",&d[i],&l[i],&r[i]);
int begin=1,end=m;
if(isok(m)){cout<<"0";return 0;}
while(begin<end)
{
int mid=(begin+end)/2;
if(isok(mid))begin=mid+1;
else end=mid;
}
cout<<"-1"<<endl<<begin;
}