[ABC202E] Count Descendants - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:在dfs序中每个点进入的时间Tin,和每个点离开的时间Tout都是不同的.
那么在本题询问中给出两个参数:U和D,问有多少个距离根节点为D的点,并且点U在到根节点的路径中.根据时间戳可知,若X满足该条件,那么 TinU<=TinX && ToutU>=ToutX那么根据这个性质,可以把同一层的Tin存到vector中.因为timer是递增的,那么在记录到vector中的时候也是递增的.那么,在询问的时候可以在vector[D]中二分得到地址ToutU-TinU偏移量的值就是满足条件的个数
int n,q,timer=0;
int Tin[200005],Tout[200005]; Tin[i] 记录dfs时第一次进入i的时间. Tout[i] 记录dfs时离开i的时间.
vector<int> vct[200005];
vector<int> edge[200005];
void dfs(int cur,int d){
Tin[cur]=++timer;
vct[d].emplace_back(Tin[cur]);
for(auto v:edge[cur]) dfs(v,d+1);
Tout[cur]=++timer;
}
新知识:时间戳Tin,Tout
在dfs序中每个点进入的时间Tin,和每个点离开的时间Tout都是不同的.
那么在本题询问中给出两个参数:U和D,问有多少个距离根节点为D的点,并且点U在到根节点的路径中.
根据时间戳可知,若X满足该条件,那么 TinU<=TinX && ToutU>=ToutX
那么根据这个性质,可以把同一层的Tin存到vector中.
因为timer是递增的,那么在记录到vector中的时候也是递增的.
那么,在询问的时候可以在vector[D]中二分得到ToutU-TinU的值就是满足条件的个数
Count Descendants
https://www.luogu.com.cn/problem/AT_abc202_e
void solve(){
cin>>n;
for(int i=2;i<=n;i++){
int x; cin>>x;
edge[x].emplace_back(i);
}
dfs(1,0);
cin>>q;
while(q--){
int u,d; cin>>u>>d;
要lower_bound,因为查询的点可能是端点.
这里通过 地址 - 地址, 得到的偏移量就是满足的个数
cout<<lower_bound(vct[d].begin(),vct[d].end(),Tout[u])-lower_bound(vct[d].begin(),vct[d].end(),Tin[u])<<endl;
}
}
[ABC200D] Happy Birthday! 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:鸽巢原理..题意:在200个数字中,找不同的两组数字(长度不同or至少一个数字不同),使他们的和mod200相等. 思路:鸽巢原理:如果n个物体要放入m个抽屉中,且n>m,那么至少有一个抽屉中放有超过一个的物体。 key:若存在201个序列,那么一定其中有两个序列和对200取模相等。 对于n<=7,只需暴力枚举所有序列2^7=128,检查有没有重复的即可. 对于n>=8,是必然有解的,枚举到256个已经绰绰有余了。实际上201已经足矣. 时间复杂度o(mod)--只需要前log2(mod)+1个数字即可,后面再多数字也可以忽略不计!
int n;
int arr[250];
题意:在200个数字中,找不同的两组数字(长度不同or至少一个数字不同),使他们的和mod200相等.
思路:鸽巢原理:如果n个物体要放入m个抽屉中,且n>m,那么至少有一个抽屉中放有超过一个的物体。
key:若存在201个序列,那么一定其中有两个序列和对200取模相等。
对于n<=7,只需暴力枚举所有序列2^7=128,检查有没有重复的即可.
对于n>=8,是必然有解的,枚举到256个已经绰绰有余了。实际上201已经足矣.
时间复杂度o(mod)--只需要前log2(mod)+1个数字即可,后面再多数字也可以忽略不计!
Happy Birthday! 2
https://www.luogu.com.cn/problem/AT_abc200_d
void solve(){ 补E
cin>>n;
n=min(8ll,n); 细节
这一步是非常必要的,不然分类讨论n<=7和n>=8的话代码会非常冗余.
如果想着取min的话,n太大会导致(1<<n)==0;所以干脆在这里取个min
vector<int> ans[256]; 201足矣
unordered_map<int,int> mp; mp1<余数,序列>
for(int i=1;i<=n;i++) cin>>arr[i];
for(int i=1;i<=(1<<n)-1;i++){
int res=0;
for(int j=0;j<min(8ll,n);j++){ 这里取min
if((i>>j)&1) {
(res+=arr[j+1])%=200;
ans[i].emplace_back(j+1);
}
}
if(!mp[res]) mp[res]=i;
else{
cout<<"Yes"<<endl;
int a1=mp[res],a2=i;
sort(ans[a1].begin(),ans[a1].end());
sort(ans[a2].begin(),ans[a2].end());
cout<<ans[a1].size()<<" ";
for(auto a:ans[a1]) cout<<a<<" ";
cout<<endl;
cout<<ans[a2].size()<<" ";
for(auto a:ans[a2]) cout<<a<<" ";
cout<<endl;
return;
}
}
cout<<"No";
}
[ABC204E] Rush Hour 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:最短路变种,推导一下公式可以得到经过一条边的最短时间是什么.正常跑dijkstra即可.
const int N=100005;
const int inf=LONG_LONG_MAX/2;
typedef struct node{
int to,c,d;
};
int n,m;
priority_queue<pair<int,int>> pq;
vector<node> vct[N];
int dis[N];
bool vis[N];
void dijkstra(int s){
for(int i=1;i<=n;i++) dis[i]=inf;
dis[s]=0;
pq.emplace(0,s);
while(pq.size()){
int from=pq.top().second;
pq.pop();
if(vis[from]) continue;
vis[from]=1;
for(auto v:vct[from]){
int to=v.to,w;
int C=v.c,D=v.d;
Wmin=Ci-t-1+Di/(t0+t+1)+t0+t+1.
配凑之后,令X=t0+t+1,其中t为当前时间,t0为停留时间
则Wmin=Ci-t-1+Di/X+X
其中Ci-t-1为常数,Di/X+X为对勾函数,在X=sqrt(Di)时取得最小值.
但是要保证:出发时间(t0+t)>=当前时间dis[from],即sqrt(Di)-1>=dis[from]
计算Wmin不能用二分,因为不是单调的。应该也许是三分。但是也可以直接算出来。
虽然下面式子看着有点乱,但是只要一步一步推清楚上面的式子,是易懂的,不难的。
因为是停留整数秒,那么X应该是整数的,但是sqrt(D)不一定是整数。
严谨一点的话应该w的计算应该在代入floor(sqrt(D)),ceil(sqrt(D))中取min,而不是用round(sqrt(D))
if(sqrt(D)-1>=dis[from]) w=min(C-dis[from]-1+D/floor(sqrt(D))+floor(sqrt(D)),C-dis[from]-1+D/ceil(sqrt(D))+ceil(sqrt(D)));
else w=C+D/(dis[from]+1);
if(dis[from]+w<dis[to]){
dis[to]=dis[from]+w;
pq.emplace(-dis[to],to);
}
}
}
}
Rush Hour 2--小小配凑个式子,发现是对勾函数,代入最小值sqrt(D)
https://www.luogu.com.cn/problem/AT_abc204_e
void solve(){ G--最短路变种--有重边(正常使用)和自环(忽略)
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,c,d;
cin>>u>>v>>c>>d;
vct[u].emplace_back((node){v,c,d});
vct[v].emplace_back((node){u,c,d});
}
dijkstra(1);
if(dis[n]==LONG_LONG_MAX/2) cout<<"-1";
else cout<<dis[n];
}
SMU 夏季 2024 比赛第 3 轮 - SMUOJ --- 搬寝室 - SMUOJ
思路:简单dp. dp[i][j]定义为考虑前i个物品,拿了j对物品,最小的疲劳值为dp[i][j].
dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+w); 当前状态由前两个转移来
int n,k;
int arr[2005];
int dp[2005][1005]; dp[i][j]定义为考虑前i个物品,拿了j对物品,最小的疲劳值为dp[i][j]
void init(){
for(int i=0;i<=n;i++){ 从0开始.
for(int j=1;j<=k;j++){
dp[i][j]=LONG_LONG_MAX/2; 因为后面进行了加运算,LONG_LONG_MAX会溢出;不确定INT_MAX够不够.
}
}
}
key:易知在有序列a,b,c,d中,最小的疲劳值为(a-b)^2+(c-d)^2..dp是建立在此基础上的.
搬寝室
void solve(){ 补C--dp--o(n*k)
不难的dp,大概想到了dp数组的定义,但是只是死板的,还是不确定,没往下写
while(cin>>n>>k){ 逆天..是多组样例..题目没说。。
init();
for(int i=1;i<=n;i++) cin>>arr[i];
sort(arr+1,arr+n+1);
for(int i=2;i<=n;i++){
int w=(arr[i]-arr[i-1])*(arr[i]-arr[i-1]);
for(int j=1;j<=k;j++){
if(2*j>i) break; 不合法的条件不进行下去
dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+w);
}
}
cout<<dp[n][k]<<endl;
}
}
SMU 夏季 2024 比赛第 3 轮 - SMUOJ --- 寻找素数对 - SMUOJ
思路:WA的有点奇怪的一题.原本写的是两for循环枚举两个质数。虽然暴力了点,但是不知道为什么会WA,可能是TLE报了WA. 改成枚举一个,然后另一个算出来检查是不是质数就A了.
const int maxn=1e6+10;
int primes[maxn+10],idx=0;
bool mark[maxn+10];
void get_primes(){
for(int i=2;i<maxn;i++){
if(!mark[i]) primes[++idx]=i;
for(int j=1;j<=idx;j++){
if(i*primes[j]>maxn) break;
mark[i*primes[j]]=1;
if(i%primes[j]==0) break;
}
}
}
void solve(){ A--寻找素数对--????..
用两个for来依次枚举质数的话会wa。但是现在只枚举一个,另一个用mark[x-primes[i]]来确定又能ac。
get_primes();
//cout<<primes[idx];
int x;
while(cin>>x){
int mindif=INT_MAX;
pair<int,int> ans={0,0};
for(int i=1;i<=idx;i++){
if(primes[i]>x) break;
int num=x-primes[i];
if(!mark[num]&&abs(num-primes[i])<mindif){
mindif=abs(x-primes[i]-primes[i]);
ans={min(num,primes[i]),max(num,primes[i])};
}
}
cout<<ans.first<<" "<<ans.second<<endl;
}
}