感謝三位學長林煜傑、吳威錡、霍朝元提供各種好題目還有協助出題,沒有你們這個比賽不會辦成。
感謝tester abc864197532對於題敘跟難度的寶貴意見。
感謝幹部陳連恩協助測題並提供神奇唬爛想法。
A | B | C | D | E | F | G |
---|---|---|---|---|---|---|
18 | 16 | 3 | 11 | 11 | 0 | 0 |
Idea: niter
Task Prepration: niter
Statement Writer: niter
none
實體組首殺:snorlax_snorlax
線上組首殺:raypeng1729
題目給的數列為:
重排表格之後(數字
數字 | 字串 |
---|---|
87 | Enjoy |
48 | Your |
16 | Pizza |
6 | And |
21 | Have |
70 | Fun |
40 | ! |
輸出內容:EnjoyYourPizzaAndHaveFun!
Idea: niter
Task Prepration: niter
Statement Writer: niter
bitmask, greedy
實體組首殺:sakinu_ruigao_9487
線上組首殺:raypeng1729
因為相同數字的OR值等於自己,所以取
結論就是:此題等價於問你從集合中取一個非空子集合的OR值的最大及最小值。
記得開IO優化,沒開而吃TLE不負責。
#include <bits/stdc++.h>
#define loop(i,a,b) for(int i=a;i<b;i++)
using namespace std;
int a[200005];
int ans, ans_min;
int n;
void recursive_solve(int ind, int now, bool have_one){
if(ind == n){
ans = max(ans, now);
if(have_one) ans_min = min(ans_min, now);
return;
}
recursive_solve(ind + 1, now | a[ind], true);
recursive_solve(ind + 1, now, have_one);
}
void solve(){
ans_min = (1 << 30);
ans = 0;
loop(i,0,n){
cin >> a[i];
}
recursive_solve(0, 0, false);
cout << ans << ' ' << ans_min << '\n';
}
int main(){
int t;
cin >> t;
loop(i,0,t){
cin >> n;
solve();
}
}
觀察到一堆數字OR起來只會變大不會變小,所以最小值即為所有數字中最小的數、最大值即為全部都OR起來所得到的值。
#include <bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin >> n;
int ans_min = (1 << 30), ans_max = 0, inp;
for(int i = 0; i < n; i++){
cin >> inp;
ans_min = min(ans_min, inp);
ans_max |= inp;
}
cout << ans_max << " " << ans_min << "\n";
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
int t;
cin >> t;
for(int i = 0; i < t; i++){
solve();
}
}
Idea: wcwu
Task Prepration: wcwu
Statement Writer: wcwu
binary search, math
可以用手畫。
時間複雜度:
注意到所求的
由於我們填的正方形長度嚴格遞減,在將正方形填入土地時若遇到需轉彎的情況,可以發現新填入的正方形絕不會與轉彎前的邊上填入的正方形重疊。
令
由以上兩個結論,我們知道對於每一個邊填入正方形時,只需考慮同一水平方向的空間是否足夠即可,換句話說我們只需對每一個邊檢查其長度最多能填幾個正方形的邊長。
對於一個給定的
在此子題中,我們可以從
時間複雜度:
又或者你可以暴力枚舉轉彎時正方形的長度,再計算四個邊長的最大值即可。
時間複雜度:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,n) for(ll i=0;i<n;i++)
void solve() {
ll n;
cin>>n;
if(n==1) {
cout<<"1\n";
return;
}
ll ans=1, cur, now;
for(ll i=n*(n+1)/2;i>=1;i--) {
ll k=i;
cur=n, now=n;
while(now && cur<=k) {
now--;
cur+=now;
}
if(cur>k) {cur=now+1; now++;}
while(now && cur<=k) {
now--;
cur+=now;
}
if(cur>k) {cur=now+1; now++;}
while(now && cur<=k) {
now--;
cur+=now;
}
if(cur>k) {cur=now+1; now++;}
if(now==0) continue;
if((cur+1)*cur/2+n>k) {
ans=k+1;
break;
}
}
cout<<ans<<"\n";
}
signed main() {
int T=1;
cin>>T;
while(T--) solve();
}
觀察到如果
時間複雜度:
因為
所以可以用二分搜來計算填幾個正方形之後要轉彎。因此僅需
時間複雜度:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,n) for(ll i=0;i<n;i++)
ll binary_search(ll len, ll now) {
ll l=1, r=now;
while(l<r) {
ll mid=(l+r)>>1;
if((now+mid)*(now-mid+1)/2<=len) {
r=mid;
}
else l=mid+1;
}
return l;
}
void solve() {
ll n;
cin>>n;
if(n==1) {
cout<<"1\n";
return;
}
ll l=1, r=(ll)500000000500000000;
while(l<r) {
ll mid=(l+r)>>1;
ll now=n;
now=binary_search(mid, now);
now=binary_search(mid, now);
now=binary_search(mid, now);
if((1+now)*now/2<=mid-n) r=mid;
else l=mid+1;
}
cout<<l<<"\n";
}
signed main() {
int T=1;
cin>>T;
while(T--) solve();
}
Idea: wcwu
Task Prepration: wcwu
Statement Writer: wcwu
graph, dfs
實體組首殺:electromagnetism
線上組首殺:raypeng1729
暴力枚舉群組內詢問成績的順序,然後對所有順序找出所求的最小值即可。
時間複雜度:
把每個人視為一個點,如果任兩者之間有其中一科成績相同,則在兩點之間建一條邊,代表彼此可以問到彼此的分數。
可以用兩個迴圈跑過所有的點,藉此建出所有點之間的邊,接著透過dfs或並查集來找出LCB無法走到哪些點即可。
時間複雜度:
如果把一個人都視為點的話,光建邊就至少需要花
接著對於同一個點上的所有人,我們只需建一條鍊來讓他們彼此是連通的即可,接下來就可以透過一次 dfs 或並查集來完成這題。
時間複雜度:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int MOD1=1e9+7;
#define IOS ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,n) for(ll i=0;i<n;i++)
const int N=2e5+5;
vector<ll> G[N];
bool vis[N];
vector<ll> score[6][15];
void dfs(ll u) {
for(auto v:G[u]) {
if(!vis[v]) {
vis[v]=1;
dfs(v);
}
}
}
void solve() {
ll n;
cin>>n;
rep(i, n) {
G[i].clear();
vis[i]=0;
}
rep(i, 6) rep(j, 15) score[i][j].clear();
rep(i, n) {
rep(j, 6) {
ll x;
cin>>x;
score[j][x-1].pb(i);
}
}
rep(i, 6) {
rep(j, 15) {
if(score[i][j].empty()) continue;
ll now=score[i][j][0];
for(ll k=1;k<(ll)score[i][j].size();k++) {
G[now].pb(score[i][j][k]);
G[score[i][j][k]].pb(now);
now=score[i][j][k];
}
}
}
vis[0]=1;
dfs(0);
ll ans=0;
rep(i, n) {
if(!vis[i]) ans++;
}
cout<<ans<<"\n";
}
signed main() {
IOS
int T=1;
cin>>T;
while(T--) solve();
}
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
#define IO ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,a) for(int i=1;i<=a;i++)
#define rep0(i,a) for(int i=0;i<a;i++)
vector<int>all[7][20];
int dsu[200005], sz[200005];
int find(int g){
if(g==dsu[g]) return g;
dsu[g]=find(dsu[g]);
return dsu[g];
}
void un(int g,int h){
g=find(g),h=find(h);
if(g==h) return;
dsu[g]=h,sz[h]+=sz[g];
}
void solve()
{
rep(i,6) rep(j,15) all[i][j].clear();
int n;
cin>>n;
rep(i,n) dsu[i]=i,sz[i]=1;
rep(i,n){
rep(j,6){
int g;
cin>>g;
all[j][g].pb(i);
}
}
rep(i,6){
rep(j,15){
if(!all[i][j].size()) continue;
int k=all[i][j].back();
all[i][j].pop_back();
for(int kk:all[i][j]) un(kk,k);
}
}
int ans=sz[find(1)];
cout<<n-ans<<"\n";
}
signed main()
{
IO
int t;
cin>>t;
while(t--) solve();
return 0;
}
Idea: Fysty
Task Prepration: Fysty
Statement Writer: Fysty
data structure
實體組首殺:CYhuang
線上組首殺:raypeng1729
線性掃過陣列,同時用一個大小為
時間複雜度:
直接開一個大小為值域的陣列
對於每個
最後的答案即為
題目要求:
改寫可得:
這可以用BIT維護(由
因為
時間複雜度:
Idea: Aestivate
Task Prepration: wcwu
Statement Writer: wcwu
dp
兩個子序列分別為「全部都是
發現
在轉移時可以著重在第二個子序列,考慮枚舉
複雜度 :
因為題目對於子序列的要求只限於「相鄰兩項」,所以一個數字能不能接在子序列最後面只受到這個子序列最後面的值影響。
再加上
設
轉移時,先把所有的
總時間複雜度:
Idea: Fysty
Task Prepration: Fysty
Statement Writer: Fysty
math, number theory
有幾個偏門的酷酷方法:砸python、用hash
對於正整數
對於每個
時間複雜度:
觀察到若
假設
我們可以
假設每個
注意到因為
時間複雜度為
瓶頸是算出
我們希望解決以下問題:
給定
對於一個集合
考慮排容原理,我們其實就是要算
所以我們只需要能快速算
注意到若
所以我們只需要算滿足
整個排容只需要做位元 dp 即可,因此時間複雜度為
回到原題,我們只需要算出
連
注意到
因為
令
轉移式很簡單直接,這裡就不贅述。注意到可以滾動,然後狀態必須要放在 map 或是一個 vector 裡。
所以我們就可以在
你以為結束了嗎,還有一個問題:因為
再次考慮排容,排容真好用。
令
顯然有
還沒完,我們需要知道滿足
總時間複雜度為
原本只想出到
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int MOD=998244353;
#define MottoHayaku ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define F first
#define S second
#define pb push_back
ll val[50],cnt[50];
void calc(ll n,ll m)//credit: abc
{
ll B=m*n;
vector<vector<pll> > cur(n+1);
rep1(i,n)
{
rep1(x,i-1)
{
vector<pll> nxt;
for(auto &p:cur[x])
{
__int128 lcm=(__int128)p.F/__gcd(p.F,(ll)i)*i;
if(lcm<=B) nxt.pb({lcm,(MOD-p.S%MOD)%MOD});
}
for(auto &p:cur[x]) nxt.pb(p);
sort(nxt.begin(),nxt.end());
cur[x].clear();
int pos=0;
while(pos<nxt.size())
{
ll sum=0;
int to=pos;
while(to<nxt.size()&&nxt[pos].F==nxt[to].F)
{
sum=(sum+nxt[to].S)%MOD;
to++;
}
if(sum!=0)
{
cur[x].pb({nxt[pos].F,sum});
val[i]=(val[i]+(m/(nxt[pos].F/x))%MOD*sum%MOD+MOD)%MOD;
}
pos=to;
}
}
cur[i].pb({i,1});
val[i]=(val[i]+m)%MOD;
}
}
void solve()
{
ll n,m;
cin>>n>>m;
if(n==1)
{
cout<<"1\n";
return;
}
ll t=__lg(n);
calc(t,m);
rep1(i,t)
{
ll l=1,r=n;
while(l<r)
{
ll mid=(l+r+1)/2;
ll cur=1;
bool can=1;
rep(j,i)
{
if(cur>n/mid)
{
can=0;
break;
}
cur*=mid;
}
if(can) l=mid;
else r=mid-1;
}
cnt[i]=l-1;
}
for(int i=t;i>=1;i--) for(int j=i+i;j<=t;j+=i) cnt[i]-=cnt[j];
rep1(i,t-1) cnt[i]-=cnt[i+1];
ll ans=1;
rep1(i,t) ans=(ans+cnt[i]%MOD*val[i])%MOD;
cout<<ans<<"\n";
}
signed main()
{
MottoHayaku
int t;
//cin>>t;
t=1;
while(t--)
{
solve();
}
}