2025-12-22 hetao1733837的笔记
我决定不浪费好题了。直接抄的高消没啥意义,所以,应当借助 AI 学习。
普通高消我认为我已经会了,所以,应该去写几道题,熟悉一下板子。
DeepSeek 在《算法竞赛》题单里挑了下面这题。
普通方程组
LG4035 [JSOI2008] 球形空间产生器
原题链接:[JSOI2008] 球形空间产生器
分析
我似乎并不见得能列出方程,但是,我有一种感觉就是,要求的坐标到这些点的距离都是相等的。
呃......好像有方程了,但并非是一次的。即我们设球心坐标 ( p a n s , 1 , p a n s , 2 , p a n s , 3 , ... , p a n s , n ) (p_{ans,1}, p_{ans,2},p_{ans,3},\dots,p_{ans,n}) (pans,1,pans,2,pans,3,...,pans,n),则
( p 1 , 1 − p a n s , 1 ) 2 + ( p 1 , 2 − p a n s , 2 ) 2 + ⋯ + ( p 1 , n − p a n s , n ) = ( p 1 , 1 − p a n s , 1 ) 2 + ( p 1 , 2 − p a n s , 2 ) 2 + ⋯ + ( p 1 , n − p a n s , n ) = ( p 2 , 1 − p a n s , 1 ) 2 + ( p 2 , 2 − p a n s , 2 ) 2 + ⋯ + ( p 2 , n − p a n s , n ) ... = ( p n + 1 , 1 − p a n s , 1 ) 2 + ( p n + 1 , 2 − p a n s , 2 ) 2 + ⋯ + ( p n + 1 , n − p a n s , n ) (p_{1, 1}-p_{ans,1})^2+(p_{1, 2}-p_{ans,2})^2+\dots +(p_{1,n}-p_{ans,n}) \\=(p_{1, 1}-p_{ans,1})^2+(p_{1, 2}-p_{ans,2})^2+\dots +(p_{1,n}-p_{ans,n}) \\=(p_{2, 1}-p_{ans,1})^2+(p_{2, 2}-p_{ans,2})^2+\dots +(p_{2,n}-p_{ans,n}) \\\dots \\=(p_{n+1, 1}-p_{ans,1})^2+(p_{n+1, 2}-p_{ans,2})^2+\dots +(p_{n+1,n}-p_{ans,n}) (p1,1−pans,1)2+(p1,2−pans,2)2+⋯+(p1,n−pans,n)=(p1,1−pans,1)2+(p1,2−pans,2)2+⋯+(p1,n−pans,n)=(p2,1−pans,1)2+(p2,2−pans,2)2+⋯+(p2,n−pans,n)...=(pn+1,1−pans,1)2+(pn+1,2−pans,2)2+⋯+(pn+1,n−pans,n)
呃,这对吗?呃, p p p 数组似乎是已知的,则,似乎可以得到关于 p a n s , 1 , p a n s , 2 , p a n s , 3 , ... , p a n s , n p_{ans,1}, p_{ans,2},p_{ans,3},\dots,p_{ans,n} pans,1,pans,2,pans,3,...,pans,n 的方程,然后,暴力相减即可消去 ( p a n s , k ) 2 (p_{ans,k})^2 (pans,k)2,移项即可将常数放到右边。那就做完了?卧槽,我能写蓝了?
卧槽了,居然有提示/ll
这么贴心,居然给了 n + 1 n+1 n+1 组已知位置,这样就好写很多了。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
const double eps = 1e-8;
int n;
double a[N][N];
double p[N][N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n + 1; i++){
for (int j = 1; j <= n; j++){
cin >> p[i][j];
}
}
for (int i = 1; i <= n; i++){
double sum1 = 0.0, sum2 = 0.0;
for (int j = 1; j <= n; j++){
a[i][j] = 2.0 * (p[i + 1][j] - p[1][j]);
sum1 += p[i + 1][j] * p[i + 1][j];
sum2 += p[1][j] * p[1][j];
}
a[i][n + 1] = sum1 - sum2;
}
for (int i = 1; i <= n; i++){
int maxn = i;
for (int j = i + 1; j <= n; j++){
if (fabs(a[j][i]) > fabs(a[maxn][i])){
maxn = j;
}
}
for (int j = 1; j <= n + 1; j++){
swap(a[i][j], a[maxn][j]);
}
double div = a[i][i];
for (int j = i; j <= n + 1; j++){
a[i][j] /= div;
}
for (int j = 1; j <= n; j++){
if (j != i){
double mul = a[j][i];
for (int k = i; k <= n + 1; k++){
a[j][k] -= a[i][k] * mul;
}
}
}
}
for (int i = 1; i <= n; i++){
cout << fixed << setprecision(3) << a[i][n + 1] << " ";
}
}
呃,依然没调出来......
不说了,现在开始,认真学习高消求取异或方程组。
异或方程组/模 2 线性方程组
mhh 咋这么强,这都知道!
那么,异或方程就相当于模2方程,则加减都是异或,乘是按位与,没了......呃......
需要强调的是,系数、未知数、方程右边的数都应给 ∈ { 0 , 1 } \in \{0,1\} ∈{0,1}。
LG2447 [SDOI2010] 外星千足虫
原题链接:[SDOI2010] 外星千足虫
分析
废话不多说,直接上题目。
嗯,确实很板,但是,我并非会做啊......喔,这个电脑用暗色模式+金黄色字体真得漂亮!
呃,由于千足虫足数并不重要,只需要知道是奇数还是偶数即可,不妨看作 0 / 1 0/1 0/1,但是吧,又有了一个问题就是怎么判定到那一组可以判断全部......难道是所有位置都出现了?似乎不太对。
这个方程是极其简单的,题目已经喂到嘴边了。花花,你的线性基害人不浅/ll
呃,算是讲明白了,也就是说,这题的坑点在于我们怎么求这个尽可能小的 k k k,在高消的过程中,我们尽可能选取编号小的消,消不掉则此时有多组解,继续往后添加即可,由于 O ( n 3 ) O(n^3) O(n3) 跑不下,故使用 bitset。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
string s;
bitset<N> mat[N << 1];
int gauss(int n, int m){
int k = -1;
for (int i = 1; i <= n; i++){
int cur = i;
while (cur <= m && !mat[cur].test(i))
cur++;
if (cur > m)
return 0;
k = max(k, cur);
if (cur != i)
swap(mat[cur], mat[i]);
for (int j = 1; j <= m; j++){
if (i != j && mat[j].test(i))
mat[j] ^= mat[i];
}
}
return k;
}
int n, m;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
int val;
cin >> s >> val;
for (int j = 0; j < n; j++){
if (s[j] == '1')
mat[i].set(j + 1, 1);
else
mat[i].set(j + 1, 0);
}
mat[i].set(0, val);
}
int k = gauss(n, m);
if (k){
cout << k << '\n';
for (int i = 1; i <= n; i++){
if (mat[i].test(0))
cout << "?y7M#" << '\n';
else
cout << "Earth" << '\n';
}
}
else
cout << "Cannot Determine";
}
呃,我并非很理解吧......
模线性方程组
POJ2947 Widget Factory
原题链接:Widget Factory
分析
显然,昨天被我一眼盯出来是取模了,方程其实很好列,就是他做的那些东西前面系数定为 1 1 1,否则为 0 0 0,等式右边就是总天数对 7 7 7 取模后的天数。等一下,要是我周日入职,周一离职怎么办?一样吗?我决定学习一下题解。
都是啥啊?
正解
cpp
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
map<string,int> data;
const int MAXN = 350;
int a[MAXN][MAXN];
int b[MAXN],ans[MAXN];
int n,m,x,num;
string s,e;
void init(){
data["MON"] = 1; data["TUE"] = 2;
data["WED"] = 3; data["THU"] = 4;
data["FRI"] = 5; data["SAT"] = 6;
data["SUM"] = 7;
}
void solve(){
int i = 1,j = 1,l,x,y;
while(i <= m && j <= n){
for(l = i;l <= m;l++){
if(a[l][j]) break;
}
if(l > m){
j++;
continue;
}
for(int k = n;k;k--) swap(a[l][k],a[i][k]);
swap(b[l],b[i]);
x = a[i][j];
for(l = i+1;l <= m;l++){
y = a[l][j];
for(int k = j;k <= n;k++){
a[l][k] = (a[l][k]*x - a[i][k]*y)%7;
}
b[l] = (b[l]*x - b[i]*y)%7;
}
i++,j++;
}
for(l = i;l <= m;l++)
if(b[l]){ puts("Inconsistent data."); return; }
if(i != j || j < n){ puts("Multiple solutions."); return;}
int k;
for(i--,j--;i;i--,j--)
{
for(l=0,k=j+1;k<=n;k++) l+=a[i][k]*ans[k];
for(k=3;k<=9;k++)
if((a[i][j]*k%7+7)%7==((b[i]-l)%7+7)%7) ans[i]=k;
}
for(i = 1;i < n;i ++) printf("%d ",ans[i]);
cout << ans[i] << endl;
}
int main(){
init();
while (scanf("%d%d",&n,&m) && n && m){
memset(a,0,sizeof a);
memset(b,0,sizeof b);
memset(ans,0,sizeof ans);
for (int i = 1; i <= m;i++){
scanf("%d", &num);
cin >> s >> e;
b[i] = (data[e] - data[s] + 8) % 7;
while(num--){
scanf("%d",&x);
a[i][x]++;
}
for(int j = 1;j <= n;j++) a[i][j] %= 7;
}
solve();
}
}
我还是不理解。
练习
POJ1487 Single-Player Games
原题链接:Single-Player Games
分析
我咋这么不理解呢?彻底放弃了。
正解
cpp
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long double LD;
const LD Eps=1e-8;
const int N=30;
int n,Case=0,now,pos[N];
char str[300];
bool free_x[N];
LD a[N][N],x[N];
void GetLn(){
while (getchar()!='\n');
}
int Match_Pracket(int now){
int len=strlen(str+1),p=0;
for (int i=now;i<=len;i++){
if (str[i]=='(')
p++;
if (str[i]==')')
p--;
if (!p)
return i;
}
return -1;
}
bool Is_Num_Part(char ch){
return ch=='-'||('0'<=ch&&ch<='9');
}
int GetNum(int now,int &Next){
int len=strlen(str+1),f=1,x=0;
if (str[now]=='-')
f=-1;
else
x=str[now]-'0';
while (Is_Num_Part(str[++now]))
x=x*10+str[now]-'0';
Next=now;
return x*f;
}
void dfs(int L,int R,LD p){
if (L>R)
return;
int tot=0,i;
for (i=L;i<=R;){
if (Is_Num_Part(str[i])){
int j,v=GetNum(i,j);
i=j,tot++;
continue;
}
if (str[i]=='('){
i=Match_Pracket(i)+1;
tot++;
continue;
}
if ('a'<=str[i]&&str[i]<='z'){
i++,tot++;
continue;
}
i++;
}
p=p/tot;
for (int i=L;i<=R;){
if (Is_Num_Part(str[i])){
int j,v=GetNum(i,j);
i=j,a[now][n]-=p*v;
continue;
}
if (str[i]=='('){
int j=Match_Pracket(i);
dfs(i+1,j-1,p);
i=j+1;
continue;
}
if ('a'<=str[i]&&str[i]<='z')
a[now][str[i]-'a']+=p;
i++;
}
}
int Gauss(){
memset(free_x,0,sizeof free_x);
memset(pos,0,sizeof pos);
int k,c;
for (k=c=0;k<n&&c<n;k++,c++){
int Mk=k;
for (int i=k+1;i<n;i++)
if (fabs(a[Mk][c])<fabs(a[i][c]))
Mk=i;
if (Mk!=k)
for (int i=c;i<=n;i++)
swap(a[Mk][i],a[k][i]);
if (fabs(a[k][c])<Eps){
k--;
free_x[c]=1;
continue;
}
pos[k]=c;
for (int i=k+1;i<n;i++)
if (fabs(a[i][c])>Eps){
for (int j=n;j>=c;j--)
a[i][j]=a[i][j]-a[k][j]/a[k][c]*a[i][c];
a[i][c]=0;
}
}
for (int i=k;i<n;i++)
if (fabs(a[i][n])>Eps)
return -1;
memset(x,0,sizeof x);
for (int i=k-1;i>=0;i--){
if (free_x[pos[i]])
continue;
LD tmp=a[i][n];
for (int j=pos[i]+1;j<n;j++){
if (fabs(a[i][j])<Eps)
continue;
if (free_x[j]){
free_x[pos[i]]=1;
break;
}
tmp-=a[i][j]*x[j];
}
if (!free_x[pos[i]])
x[pos[i]]=tmp/a[i][pos[i]];
if (fabs(x[pos[i]])<Eps)
x[pos[i]]=0;
}
return 0;
}
int main(){
while (~scanf("%d",&n)&&n){
GetLn();
memset(a,0,sizeof a);
for (now=0;now<n;now++){
a[now][now]-=1;
gets(str+1);
int pos=1;
while (str[pos]!='=')
pos++;
dfs(pos+1,strlen(str+1),1);
}
int ans=Gauss();
printf("Game %d\n",++Case);
for (int i=0;i<n;i++)
if (free_x[i])
printf("Expected score for %c undefined\n",i+'a');
else
printf("Expected score for %c = %.3Lf\n",i+'a',x[i]);
puts("");
}
return 0;
}