A
根據題面描述不難發現:若當前在第\(i\)列的某個空格上,只要第\(i+1\)列存在空格,就一定可以跳到這個空格上。只需要判斷每行是否有空格子即可。
int n;
char s[3][N];
int main()
{
int T=read();
while (T--)
{
n=read();int ans=1;
rep(i,1,2) scanf("%s",s[i]+1);
rep(i,1,n) if ((s[1][i]=='1') && (s[2][i]=='1')) ans=0;
if (ans) puts("YES");else puts("NO");
}
return 0;
}
B
枚舉兩門課分別在什么時候上,所有的人可以按照每門課是否有時間被分為四類,分別記為\(c_{00},c_{01},c_{10},c_{11}\)(其中0表示沒有時間),那么只要\(c_{00}=0,c_{01}\leq n,c_{10}\leq n\)即可,\(c_{01}\)與\(c_{10}\)不足\(n\)的部分可以由\(c_{11}\)補上。
int n,a[N][6];
int main()
{
int T=read();
while (T--)
{
n=read();
rep(i,1,n) rep(j,1,5) a[i][j]=read();
if (n&1) {puts("NO");continue;}
int ans=0;
rep(i,1,5) rep(j,i+1,5)
{
int ok=1,c1=0,c2=0,c3=0;
rep(k,1,n)
{
if ((a[k][i]) && (a[k][j])) c1++;
else if ((!a[k][i]) && (a[k][j])) c2++;
else if ((a[k][i]) && (!a[k][j])) c3++;
else {ok=0;break;}
}
if ((ok) && (c2<=n/2) && (c3<=n/2)) {ans=1;break;}
}
if (ans) puts("YES");else puts("NO");
}
return 0;
}
C
不難發現刪去的這兩個數的和為定值,開個桶之后直接算答案就可以了。
int n,a[N];
map<int,int> mp;
int main()
{
int T=read();
while (T--)
{
n=read();mp.clear();
ll sum=0;
rep(i,1,n) {a[i]=read();sum+=a[i];mp[a[i]]++;}
if ((sum*2)%n) {puts("0");continue;};
ll g=sum*2/n;
ll ans=0;
rep(i,1,n)
{
ans+=mp[g-a[i]];
if (g-a[i]==a[i]) ans--;
}
printf("%lld\n",ans/2);
}
return 0;
}
D
考慮用總數減去不合法的情況。把所有的\((a_i,b_i)\)當成二維坐標軸上的點,由於不存在相同的\((a_i,b_i)\),所以一個方案不合法當且僅當這三個點構成了"L"形。枚舉中間的拐點,通過二分或其它手段算出在拐點上下左右有多少個點,然后用乘法原理乘一乘就可以了。
#define pbk push_back
int n,a[N],b[N];
vi pa[N],pb[N];
int main()
{
int T=read();
while (T--)
{
n=read();
rep(i,1,n)
{
a[i]=read();b[i]=read();
pa[a[i]].pbk(b[i]);
pb[b[i]].pbk(a[i]);
}
ll ans=1ll*n*(n-1)*(n-2)/6;
rep(i,1,n)
{
int p1=lower_bound(pa[a[i]].begin(),pa[a[i]].end(),b[i])-pa[a[i]].begin(),
p2=lower_bound(pb[b[i]].begin(),pb[b[i]].end(),a[i])-pb[b[i]].begin();
int s1=pa[a[i]].size(),s2=pb[b[i]].size();
ll a1=p1,b1=p2,a2=s1-p1-1,b2=s2-p2-1;
ans-=(a1*b1+a2*b1+a1*b2+a2*b2);
}
printf("%lld\n",ans);
rep(i,1,n)
{
pa[a[i]].clear();
pb[b[i]].clear();
}
}
return 0;
}
E
首先各憑本事算一下空白的\(n\times m\)方格里有多少合法路徑。
每次將一個格子的狀態翻轉時,考慮影響到的路徑條數:這樣的路徑一定會經過這個點。把影響到的路徑根據這個點拆成前后兩部分,前半部分和后半部分的數量分別可以通過bfs得到。之后再算出兩個部分拼起來的路徑數即可。
int n,m,q,ban[N][N];
ll f[N][N][2];
int query(int x,int y,int op1,int d)
{
int ans=1;
while (1)
{
if (op1) y+=d;else x+=d;
if ((x>=1) && (x<=n) && (y>=1) && (y<=m) && (!ban[x][y]))
{
ans++;op1^=1;
}
else return ans;
}
}
int main()
{
n=read();m=read();q=read();
ll ans=0;
rep(i,1,n) rep(j,1,m)
{
f[i][j][0]=f[i-1][j][1]+1;
f[i][j][1]=f[i][j-1][0]+1;
ans+=(f[i][j][0]+f[i][j][1]-1);
}
while (q--)
{
int x=read(),y=read();
int a1=query(x,y,0,-1),b1=query(x,y,1,-1),
a2=query(x,y,0,1),b2=query(x,y,1,1);
ban[x][y]^=1;
int now=a1*b2+a2*b1-1;
if (ban[x][y]) ans-=now;
else ans+=now;
printf("%lld\n",ans);
}
return 0;
}
F
不知道為啥\(O(n2^n\log s_i)\)在cf上跑的飛快/wul
任意的括號序列通過不斷消去匹配括號,最終可以形成))…)((…(
的形式,根據括號個數可以用一個二元組\((a,b)\)表示。對於每一個括號序列,預處理出每個前綴對應的二元組(這個顯然),和從當前下標開始的后綴中有多少個合法前綴(利用類似dp的思想,每次找一個最短的合法前綴跳過去),之后會用上。
看見\(n\)這么小考慮狀壓dp:記\(f_{S}\)為使用\(S\)中元素組成括號序列的答案。轉移時枚舉最終的括號序列中最后的元素\(i\),注意到如果一個序列存在未匹配的)
,那么無論在它的后面接上啥序列都不可能再構成合法前綴,因此對\(S\setminus \{i\}\)所構成的括號序列對應的二元組\((a,b)\)可以被唯一確定下來,之后利用這個二元組便可以找到\(s_i\)的最短前綴使得加上這個前綴后的括號序列都是合法序列(利用預處理的前綴二元組)。綜上,加上\(s_i\)后給當前括號序列帶來的貢獻就是\(1+s_i\)的一個后綴的合法前綴數量(這個也是預處理過的)。同時,如果在加上了\(s_i\)后的串出現了未匹配的)
,那么這個串之后無論怎么添加括號都不會出現新的合法前綴,但是由於這么加可能產生最終答案所以還是要記下來。
具體的實現由於是賽時寫的+好久沒寫代碼可能有一些丑陋,dp數組記了兩維表示當前括號序列能否繼續添加后綴時的答案,同時轉移為了好寫用了map所以這東西是咋跑過去的
pii L=mkp(0,1),R=mkp(1,0),emp=mkp(0,0);
pii operator +(pii a,pii b)
{
pii c;
c.fir=a.fir;c.sec=b.sec;
if (a.sec<b.fir) c.fir+=b.fir-a.sec;
else c.sec+=a.sec-b.fir;
return c;
}
int n,f[(1<<20)+10][2],len[N];
pii sum[(1<<20)+10];
vi g[N];
vector<pii> a[N];
map<pii,int> mp[N],mmp;
char s[M];
int main()
{
n=read();
rep(i,1,n)
{
scanf("%s",s+1);
int m=strlen(s+1);len[i]=m;
a[i].resize(m+2);
a[i][0]=emp;
rep(j,1,m)
{
pii op;
if (s[j]=='(') op=L;else op=R;
a[i][j]=a[i][j-1]+op;
if (!mp[i].count(a[i][j])) mp[i][a[i][j]]=j;
}
g[i].resize(m+2);
pii now=emp;mmp.clear();
mmp[emp]=m+1;
per(j,m,1)
{
pii op;
if (s[j]=='(') op=L;else op=R;
now=op+now;
if ((s[j]=='(') && (mmp.count(now)))
{
int p=mmp[now];
g[i][j]=g[i][p]+1;
}
mmp[now]=j;
}
}
rep(pre,0,(1<<n)-1)
{
rep(i,1,n)
{
if ((pre>>(i-1))&1) continue;
int now=pre^(1<<(i-1));
sum[now]=sum[pre]+a[i][len[i]];
int mn=min(sum[now].fir,sum[now].sec);
sum[now].fir-=mn;sum[now].sec-=mn;
}
}
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
rep(pre,0,(1<<n)-1)
{
if (sum[pre].fir) continue;
rep(i,1,n)
{
if ((pre>>(i-1))&1) continue;
int sta=pre^(1<<(i-1));
pii goal=sum[pre];swap(goal.fir,goal.sec);
int tmp=f[pre][0];
if (mp[i].count(goal))
{
tmp++;
int pos=mp[i][goal];
tmp+=g[i][pos+1];
}
pii now=sum[pre]+a[i][len[i]];
if (now.fir) f[sta][1]=max(f[sta][1],tmp);
else f[sta][0]=max(f[sta][0],tmp);
}
}
int ans=0;
rep(i,0,(1<<n)-1) rep(j,0,1) ans=max(ans,f[i][j]);
printf("%d",ans);
return 0;
}